Java 超详细讲解十大排序算法面试无忧


Posted in Java/Android onApril 08, 2022

Java 超详细讲解十大排序算法面试无忧

排序算法的稳定性:

        假定在待排序的记录序列中,存在多个具有相同的关键字的记录,如果排序以后,保证这些记录的相对次序保持不变,即在原序列中,a[i]=a[j],且 a[i] 在 a[j] 之前,排序后保证 a[i] 仍在 a[j] 之前,则称这种排序算法是稳定的;否则称为不稳定的。

Java 超详细讲解十大排序算法面试无忧

一.选择排序

每次从待排序的元素中选择最小的元素,依次和第1、2、3...位置的元素进行交换。这样在数组前面的部分形成有序区域。每进行一次交换,有序区域长度加一。

Java 超详细讲解十大排序算法面试无忧

public static void selectionSort(int[] arr){
    //细节一:这里可以是arr.length也可以是arr.length-1
    for (int i = 0; i < arr.length-1 ; i++) {
        int mini = i;
        for (int j = i+1; j < arr.length; j++) {
            //切换条件,决定升序还是降序
            if(arr[mini]>arr[j]) mini =j;
        }
        swap(arr,mini,i);
    }
}

二.冒泡排序

        依次比较相邻的两个数,如果顺序错误就把他们交换过来,这样的话,每一轮比较下来都可以把最大的数放到它应该在的位置。(就像是把最大的气泡冒到最上层一样)

         这里解释一下顺序错误的含义。我们按照按升序排序,后面的值应该大于等于前面的值,如果不满足的话,就交换。

Java 超详细讲解十大排序算法面试无忧

public static void bubbleSort(int[] arr){
    for (int i = 0; i < arr.length-1; i++) {
        //记录本次有没有进行交换的操作
        boolean flag = false;
        //保存在头就动头,保存在尾就动尾
        for(int j =0 ; j < arr.length-1-i ; j++){
            //升序降序选择地
            if(arr[j] > arr[j+1])
            {
                swap(arr,j,j+1);
                flag = true;
            }
        }
        //如果本次没有进行交换操作,表示数据已经有序
        if(!flag){break;} //程序结束
    }
}

三.插入排序

        插入排序其实可以理解为我们玩扑克时摸牌的过程,我们在摸牌的时候手里的牌总是有序的,每摸一张牌,就把这张牌插到它应该在的位置。等所有的牌都摸完了以后,全部的牌就都有序了。

Java 超详细讲解十大排序算法面试无忧

 思考:数组前面形成了有序区域,那我查找当前数字应该插入位置的时候,用二分进行,是不是就可以把插入排序的复杂度优化到O(nlogn)了呀?

        二分倒是可以log的复杂度找到位置。 关键是如果用数组存储的话,插入的时候数据后移还是O(n)的复杂度。如果用链表的话,找到位置插入是O(1)的了,但是链表也没办法二分呀。

public static void insertSort(int[] arr){
        //从第二个数开始,把每个数依次插入到指定的位置
        for(int i = 1 ; i < arr.length ; i++)
        {
            int key = arr[i];
            int j = i-1;
            //大的后移操作
            while(j >= 0 && arr[j] > key)
            {
                arr[j+1] = arr[j];
                j--;
            }
            arr[j+1] = key;
        }
    }

四.希尔排序

        希尔排序是Donald Shell于1959年提出的一种排序算法,是对直接插入排序的改进之后的高效版本。 希尔排序需要准备一组数据作为增量序列。

这组数据需要满足以下三个条件:

1. 数据递减排列

2. 数据中的最大值小于待排序数组的长度

3. 数据中的最小值是1。

        只要满足上述要求的数组都可以作为增量序列,但是不同的增量序列会影响到排序的效率。这里我们用{5,3,1}作为增量序列来进行讲解

Java 超详细讲解十大排序算法面试无忧

 实现优化的原因:减少数据量,使O(n)和O(n^2)的差距并不大

public static void shellSort(int[] arr){
        //分块处理
        int gap = arr.length/2; //增量
        while(1<=gap)
        {
            //插入排序:只不过是与增量位交换
            for(int i = gap ; i < arr.length ; i++)
            {
                int key = arr[i];
                int j = i-gap;
                while(j >= 0 && arr[j] > key)
                {
                    arr[j+gap] = arr[j];
                    j-=gap;
                }
                arr[j+gap] = key;
            }
            gap = gap/2;
        }
    }

五.堆排序

是一棵完全二叉树,分为大根堆、小根堆两种

可以O(1)取最大/小值,可以O(logn)删除最大/小值,可以O(logn)插入元素

MIN-HEAPIFY(i)操作:

我们假设完全二叉树中某节点 i 的左子树和右子树都满足小根堆的性质,假设 i 节点的左孩子是 left_i,i 节点的右孩子是 rigℎt_i。那如果 a[i] 大于 a[left_i] 或 a[rigℎt_i] 的话,那以 i 节点为根节点的整棵子树就不满足小根堆的性质了,我们现在要进行一个操作:把以 i 为根节点的这棵子树调整成小根堆。

Java 超详细讲解十大排序算法面试无忧

//堆排序
    public static void heapSort(int[] arr){
        //开始调整的位置为最后一个叶子节点
        int start = (arr.length - 1)/2;
        //从最后一个叶子节点开始遍历,调整二叉树
        for (int i = start; i >= 0 ; i--){
            maxHeap(arr, arr.length, i);
        }
 
        for (int i = arr.length - 1; i > 0; i--){
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            maxHeap(arr, i, 0);
        }
    }
 
    //将二叉树调整为大顶堆
    public static void maxHeap(int[] arr, int size, int index){
        //建立左子节点
        int leftNode = 2 * index + 1;
        //建立右子节点
        int rightNode = 2 * index + 2;
 
        int maxNode = index;
        //左子节点的值大于根节点时调整
        if (leftNode < size && arr[leftNode] > arr[maxNode]){
            maxNode = leftNode;
        }
        //右子节点的值大于根节点时调整
        if (rightNode < size && arr[rightNode] > arr[maxNode]){
            maxNode = rightNode;
        }
        if (maxNode != index){
            int temp = arr[maxNode];
            arr[maxNode] = arr[index];
            arr[index] = temp;
            //交换之后可能会破坏原来的结构,需要再次调整
            //递归调用进行调整
            maxHeap(arr, size, maxNode);
        }
    }

我使用的是大根堆,排序的过程归根下来就是:先左底根后右底根(看自己怎么写)->每个根向上在向下。(左右上下)

六.归并排序

        归并排序是分治法的典型应用,先来介绍一下分治法,分治法是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题规模很小可以直接求解,再将子问题的解进行合并来得到原问题的解。

Java 超详细讲解十大排序算法面试无忧

        归并排序分解子问题的过程就是每一次把数组分成2份,直到数组的长度为1(因为只有一个数字的数组是有序的)。然后再将相邻的有序数组合并成一个有序数组。直到全部合到一起,整个数组就排序完毕了。 现在要解决的问题就是如何把两个有序的数组合并成一个有序的数组。其实就是每次比较两个数组当前最小的两个元素,哪个小就选哪个

数组a

数组b

说明

答案数组

2,5,7

1,3,4

1<2,取1,数组b的指针后移

1

2,5,7

1,3,4

2<3,取2,数组a的指针后移

1,2

2,5,7

1,3,4

3<5,取3,数组b的指针后移

1,2,3

2,5,7

1,3,4

4<5,取4,数组b的指针后移

1,2,3,4

2,5,7

1,3,4

数组b中没有元素了,取5

1,2,3,4,5

2,5,7

1,3,4

数组b中没有元素了,取7

1,2,3,4,5,7

    public static void mergeSort(int[] arr, int low, int high){
        int middle = (high + low)/2;
        if (low < high){
            //递归排序左边
            mergeSort(arr, low, middle);
            //递归排序右边
            mergeSort(arr, middle +1, high);
            //将递归排序好的左右两边合并
            merge(arr, low, middle, high);
        }
 
    }
 
    public static void merge(int[] arr, int low, int middle, int high){
        //存储归并后的临时数组
        int[] temp = new int[high - low + 1];
        int i = low;
        int j = middle + 1;
        //记录临时数组中存放数字的下标
        int index = 0;
        while (i <= middle && j <= high){
            if (arr[i] < arr[j]){
                temp[index] = arr[i];
                i++;
            } else {
                temp[index] = arr[j];
                j++;
            }
            index++;
        }
        //处理剩下的数据
        while (j <= high){
            temp[index] = arr[j];
            j++;
            index++;
        }
        while (i <= middle){
            temp[index] = arr[i];
            i++;
            index++;
        }
        //将临时数组中的数据放回原来的数组
        for (int k = 0; k < temp.length; ++k){
            arr[k + low] = temp[k];
        }
    }

七.快速排序

        快速排序的工作原理是:从待排序数组中随便挑选一个数字作为基准数,把所有比它小的数字放在它的左边,所有比它大的数字放在它的右边。然后再对它左边的数组和右边的数组递归进行这样的操作。

        全部操作完以后整个数组就是有序的了。 把所有比基准数小的数字放在它的左边,所有比基准数大的数字放在它的右边。这个操作,我们称为“划分”(Partition)。

Java 超详细讲解十大排序算法面试无忧

	//快速排序
	public static void QuickSort1(int[] arr, int start, int end){
		int low = start, high = end;
		int temp;
		if(start < end){
		    int guard = arr[start];
			while(low != high){
				while(high > low && arr[high] >= guard) high--;
				while(low < high && arr[low] <= guard) low++;
				if(low < high){
					temp = arr[low];
					arr[low] = arr[high];
					arr[high] = temp;
				}
			}
			arr[start] = arr[low];
			arr[low] = guard;
			QuickSort1(arr, start, low-1);
			QuickSort1(arr, low+1, end);
		}
	}
	
	//快速排序改进版(填坑法)
	public static void QuickSort2(int[] arr, int start, int end){
		int low = start, high = end;
		if(start < end){
		while(low != high){
	    	int guard = arr[start];//哨兵元素
			while(high > low && arr[high] >= guard) high--;
			arr[low] = arr[high];
			while(low < high && arr[low] <= guard) low++;
			arr[high] = arr[low];
		}
		arr[low] = guard;
		QuickSort2(arr, start, low-1);
		QuickSort2(arr, low+1, end);
	}

 

八.鸽巢排序

        计算一下每一个数出现了多少次。举一个例子,比如待排序的数据中1出现了2次,2出现了0次,3出现了3次,4出现了1次,那么排好序的结果就是{1.1.3.3.3.4}。

Java 超详细讲解十大排序算法面试无忧

    //鸽巢排序
    public static void PigeonholeSort(int[] arr){
        //获取最大值
        int k = 0;
        for (int i = 0; i < arr.length; i++) {
            k = Math.max(k,arr[i]);
        }
        //创建计数数组并且初始化为0
        int[] cnt = new int[k+10];
        for(int i = 0 ; i <= k ; i++) { cnt[i]=0; }
        for(int i = 0 ; i < arr.length ;i++) { cnt[arr[i]]++; }
        int j = 0;
        for(int i = 0 ; i <=k ; i++)
        {
            while(cnt[i]!=0)
            {
                arr[j]=i;
                j++;
                cnt[i]--;
            }
        }
    }

 

        鸽巢排序其实算不上是真正意义上的排序算法,它的局限性很大。只能对纯整数数组进行排序,举个例子,如果我们需要按学生的成绩进行排序。是一个学生+分数的结构那就不能排序了。

九.计数排序

        先考虑这样一个事情:如果对于待排序数据中的任意一个元素 a[i],我们知道有 m 个元素比它小,那么我们是不是就可以知道排好序以后这个元素应该在哪个位置了呢?(这里先假设数据中没有相等的元素)。计数排序主要就是依赖这个原理来实现的。

比如待排序数据是{2,4,0,2,4} 先和鸽巢一样做一个cnt数组{1,0,2,0,2}                                                                                                                                    0,1,2,3,4

此时cnt[i]表示数据i出现了多少次,

然后对cnt数组做一个前缀和{1,1,3,3,5}    :0,1,2,3,4

此时cnt[i]表示数据中小于等于i的数字有多少个

待排序数组

计数数组

说明

答案数组ans

2,4,0,2,4

1,1,3,3,5

初始状态

null,null,null,null,null,

2,4,0,2,4

1,1,3,3,5

cnt[4]=5,ans第5位赋值,cnt[4]-=1

null,null,null,null,4

2,4,0,2,4

1,1,3,3,4

cnt[2]=3,ans第3位赋值,cnt[2]-=1

null,null,2 null,4

2,4,0,2,4

1,1,2,3,4

cnt[0]=1,ans第1位赋值,cnt[0]-=1

0,null,2,null,4

2,4,0,2,4

0,1,2,3,4

cnt[4]=4,ans第4位赋值,cnt[4]-=1

0,null,2,4,4

2,4,0,2,4

0,1,2,3,3

cnt[2]=2,ans第2位赋值,cnt[2]-=1

0,2,2,4,4

十.基数排序

基数排序是通过不停的收集和分配来对数据进行排序的。

Java 超详细讲解十大排序算法面试无忧

  • 因为是10进制数,所以我们准备十个桶来存分配的数。
  • 最大的数据是3位数,所以我们只需要进行3次收集和分配。
  • 需要先从低位开始收集和分配(不可从高位开始排,如果从高位开始排的话,高位排好的顺序会在排低位的时候被打乱,有兴趣的话自己手写模拟一下试试就可以了)
  • 在收集和分配的过程中,不要打乱已经排好的相对位置

        比如按十位分配的时候,152和155这两个数的10位都是5,并且分配之前152在155的前面,那么收集的时候152还是要放在155之前的。

//基数排序
    public static void radixSort(int[] array) {
        //基数排序
        //首先确定排序的趟数;
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }
        int time = 0;
        //判断位数;
        while (max > 0) {
            max /= 10;
            time++;
        }
        //建立10个队列;
        List<ArrayList> queue = new ArrayList<ArrayList>();
        for (int i = 0; i < 10; i++) {
            ArrayList<Integer> queue1 = new ArrayList<Integer>();
            queue.add(queue1);
        }
        //进行time次分配和收集;
        for (int i = 0; i < time; i++) {
            //分配数组元素;
            for (int j = 0; j < array.length; j++) {
                //得到数字的第time+1位数;
                int x = array[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
                ArrayList<Integer> queue2 = queue.get(x);
                queue2.add(array[j]);
                queue.set(x, queue2);
            }
            int count = 0;//元素计数器;
            //收集队列元素;
            for (int k = 0; k < 10; k++) {
                while (queue.get(k).size() > 0) {
                    ArrayList<Integer> queue3 = queue.get(k);
                    array[count] = queue3.get(0);
                    queue3.remove(0);
                    count++;
                }
            }
 
        }
 
 
    }

到此这篇关于Java 超详细讲解十大排序算法面试无忧的文章就介绍到这了,更多相关Java 排序算法内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
手把手教你用SpringBoot将文件打包成zip存放或导出
Jun 11 Java/Android
Java输出Hello World完美过程解析
Jun 13 Java/Android
Spring整合Mybatis的全过程
Jun 28 Java/Android
SpringBoot实现quartz定时任务可视化管理功能
Aug 30 Java/Android
使用jpa之动态插入与修改(重写save)
Nov 23 Java/Android
关于@OnetoMany关系映射的排序问题,使用注解@OrderBy
Dec 06 Java/Android
Java实现给Word文件添加文字水印
Feb 15 Java/Android
Java 通过手写分布式雪花SnowFlake生成ID方法详解
Apr 07 Java/Android
Java中Dijkstra(迪杰斯特拉)算法
May 20 Java/Android
Spring Boot 的创建和运行示例代码详解
Jul 23 Java/Android
IDEA中sout快捷键无效问题的解决方法
Jul 23 Java/Android
app场景下uniapp的扫码记录
Jul 23 Java/Android
详解Alibaba Java诊断工具Arthas查看Dubbo动态代理类
SpringCloud Function SpEL注入漏洞分析及环境搭建
SpringBoot中获取profile的方法详解
Apr 08 #Java/Android
教你在 Java 中实现 Dijkstra 最短路算法的方法
Java 垃圾回收超详细讲解记忆集和卡表
Java 常见的限流算法详细分析并实现
Java 超详细讲解ThreadLocal类的使用
You might like
PHP图片处理之使用imagecopyresampled函数实现图片缩放例子
2014/11/19 PHP
yii2.0使用Plupload实现带缩放功能的多图上传
2015/12/22 PHP
linux下为php添加iconv模块的方法
2016/02/28 PHP
PHP生成和获取XML格式数据的方法
2016/03/04 PHP
javascript 触发事件列表 比较不错
2009/09/03 Javascript
爆炸式的JS圆形浮动菜单特效代码
2010/03/03 Javascript
js中settimeout方法加参数
2014/02/28 Javascript
浅析JavaScript访问对象属性和方法及区别
2015/11/16 Javascript
解决JS无法调用Controller问题的方法
2015/12/31 Javascript
jQuery插件ImgAreaSelect实现头像上传预览和裁剪功能实例讲解一
2017/05/26 jQuery
Bootstrap弹出框(Popover)被挤压的问题小结
2017/07/11 Javascript
JS实现520 表白简单代码
2018/05/21 Javascript
如何解决webpack-dev-server代理常切换问题
2019/01/09 Javascript
vue router 组件的高级应用实例代码
2019/04/08 Javascript
JS中如何轻松遍历对象属性的方式总结
2019/08/06 Javascript
使用BeautifulSoup爬虫程序获取百度搜索结果的标题和url示例
2014/01/19 Python
Python ORM框架SQLAlchemy学习笔记之映射类使用实例和Session会话介绍
2014/06/10 Python
Django开发中的日志输出的方法
2018/07/02 Python
Python K最近邻从原理到实现的方法
2019/08/15 Python
Python常用库Numpy进行矩阵运算详解
2020/07/21 Python
python制作微博图片爬取工具
2021/01/16 Python
HTML5实现视频直播功能思路详解
2017/11/16 HTML / CSS
使用Html5多媒体实现微信语音功能
2019/07/26 HTML / CSS
HealthElement海外旗舰店:新西兰大卖场
2018/02/23 全球购物
意大利香水和化妆品购物网站:Parfimo.it
2019/10/06 全球购物
Ado与Ado.net的相同与不同
2014/12/08 面试题
金鑫耀Java笔试题
2014/09/06 面试题
大学生毕业求职简历的自我评价
2013/10/24 职场文书
食品营养与检测应届生求职信
2013/11/08 职场文书
超市开业庆典策划方案
2014/05/14 职场文书
预备党员公开承诺书
2014/05/28 职场文书
2014年应急工作总结
2014/12/11 职场文书
2015出纳试用期工作总结
2014/12/12 职场文书
家长会欢迎词
2015/01/23 职场文书
《乌鸦喝水》教学反思
2016/02/19 职场文书
ECharts transform数据转换和dataZoom在项目中使用
2022/12/24 Javascript