排序算法
![](https://i-blog.csdnimg.cn/blog_migrate/9b6fce4058a424d451afc362370b0733.png)
1、插入排序
1-1直接插入排序
->思想:把待排序的数据按其值的大小插入到已经排好序的有序数组中,直到所有数据插完为止,即可得到一个有序数列。
![](https://i-blog.csdnimg.cn/blog_migrate/08fd0040090dc64a79c60c08e6819068.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7eedbbd983abb0a17f0f25a7e5950b04.png)
->时间复杂度:最坏情况下:O(N^2) ->数据逆序时
最好情况下:O(N) ->数据有序时
->稳定排序
->使用场景:数据越有序越快,适用于趋于有序的数组
1-2希尔排序【也称:缩小增量排序】
->思想:先选定一个整数n【增量】,把待排序的数据分成n个组,这些数据中所有距离为n的为一组,组内进行插入排序;然后缩小增量【n变小】,继续上述操作,直至n为1,就可排好数据。
![](https://i-blog.csdnimg.cn/blog_migrate/9667e2823ea8519b65f50a705e570d95.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3fe5570c72a195ea6c858dddd45fb924.png)
->希尔排序是对直接插入排序的优化;当增量>1时,相当于是预排序。
->gap的取值方法很多,导致很难去计算,因此在一些资料中给出的希尔排序的时间复杂度不固定
暂时的时间复杂度:O(N^1.3 ~N^1.5)
->不稳定排序
2、选择排序
2-1 直接选择排序
->思想:每次从待排的数据中选择出一个最小(降序,就选最大)的元素,放到序列的起始位置,直至所有数据被排完。
每次只排一个。
![](https://i-blog.csdnimg.cn/blog_migrate/6eced34b8eb272bcc311cf7e3a6d0c9c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6787d38e81c7d1fb4caab9fb1c542a96.png)
直接选择排序的进阶:
每次排两个。思想:与直接选择排序类似,假设要升序的数据,每次从待排的数据中选择出一个最小的元素放到序列的起始位置,同时选择一个最大的元素放于末位置,直至所有数据被排完。
实现如下:
![](https://i-blog.csdnimg.cn/blog_migrate/085df747401395cfa75a70f6912cea65.png)
上面标※的内容,是考虑到最大值就在left位置时,此时要将找到的最小值与其进行交换,会导致最大值就跑到了交换后最小值的位置,因此需要修正一下。
2-2堆排序
降序:建立小根堆
升序:建立大根堆;每次让最后一个数据与堆顶元素进行交换;交换完之后向下调整;直到所有数据交换完成
![](https://i-blog.csdnimg.cn/blog_migrate/25b882b099f1f2288b737fa34548094e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3a9128c2cb07101a7af5919192f075de.png)
->堆排序的时间复杂度:O(NlogN)
->空间复杂度:O(1)
->不稳定的排序
3、交换排序
3-1 冒泡排序
i表示趟数,每次排一个;
j每次交换找到最大的/最小的
![](https://i-blog.csdnimg.cn/blog_migrate/07d4076354a7b9abe5ce137aba07a2d9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f380c205046dbcf9a48f026846c8af1e.png)
->没优化的时间复杂度:O(N^2)
->优化后的时间复杂度:O(N)
->稳定的排序
3-2 快速排序
思想:
主框架如下图:让待排序元素的某个值作为基准值,将序列按照该基准值分为两个部分,左边是小于它的,右边是大于它的;然后左右子序列重复这个过程;直到该序列排好序即可。
![](https://i-blog.csdnimg.cn/blog_migrate/382166e7f58350f0939a7b94cfa77b07.png)
针对每种与基准值交换的方法【分割成两个序列的方法不同】:
法1:hoare
![](https://i-blog.csdnimg.cn/blog_migrate/a7c417164f3ff646929960cb81061025.png)
->最坏情况:顺序时,O(N^2)
->时间复杂度:理想情况,O(NlogN)
->使用场景:无序的情况。在有序的情况时,容易栈溢出。【递归】
法2:挖坑法
把第一个位置挖出来作为基准值,第一个位置就为坑了;从后头开始,将小于基准值的放到坑中,此时,该位置为新的坑;在从左边开始,将大于基准值的放到新坑中,此刻这个位置就是新的坑,以此类推。
![](https://i-blog.csdnimg.cn/blog_migrate/feb3c61fbfb67f05438d854c883d7fbc.png)
->快排时间复杂度:理想情况下O(NlogN);有序或者逆序时O(N^2)
->快排空间复杂度:最好O(N);最坏:O(N),当N足够大时,递归的深度就大。
解决方案【让序列尽可能均匀分割】:
1、随机选取基准法
2、三数取中法【找前中后三个位置的数,进行比较,把第二大的数作为基准】
![](https://i-blog.csdnimg.cn/blog_migrate/8dacde1ec18f21bd287f627aad86a208.png)
针对有序的数据:
![](https://i-blog.csdnimg.cn/blog_migrate/d2aaf9fe777929bd4ef194c02563ba25.png)
如果待排数据较少时,可以采用直接插入排序。
->快速排序不稳定。
非递归实现快速排序:
public static void quickSort2(int[] array){
Stack<Integer> stack = new Stack<>();
int left = 0;
int right = array.length-1;
int pivot = patrition(array,left,right);
//前边超过一个数字了就入栈
if (pivot > left+1){
stack.push(left);
stack.push(pivot-1);
}
if (pivot < right-1){
stack.push(pivot+1);
stack.push(right);
}
while (!stack.isEmpty()){
right = stack.pop();
left = stack.pop();
pivot = patrition(array,left,right);
if (pivot > left+1){
stack.push(left);
stack.push(pivot-1);
}
if (pivot < right-1){
stack.push(pivot+1);
stack.push(right);
}
}
}
4、归并排序
先分解,再合并。
![](https://i-blog.csdnimg.cn/blog_migrate/237ea2e6912c5e8a11c8e8a607c0cc2f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e476be97c51ba445ced6c4fae64363db.png)
->不论什么数组,时间复杂度:O(NlogN)
->空间复杂度:O(N)
->稳定的排序