目录
1.排序是什么?
排序就是一串记录,按照其中的某个或者某些关键字的大小,递增或递减的排列起来的操作。
2.排序的稳定性
两个相等的数据,如果经过排序,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序算法。
为什么稳定性这么重要?
3.七大排序的时间复杂度与空间复杂度
这里都是内部排序(一次性将所有待排序的数据放入内存中进行排序,基于元素之间的比较的排序)
而外部排序 :依赖硬盘(外部存储器)进行的排序算法,桶排序、基数排序、基数排序(对于数据集合的要求非常高,只能在特定的场合下使用)。时间复杂度都是O(n),应用场景非常局限,只能在特定的场景下去应用。内存放不下,需要外部存储。
器,称为海量排序算法。
4.选择排序
每次从无序区间中选择一个最大或最小值,存放在无需区间的最前或者最后的位置(此位置的元素已经有序) ,直到所有的数据都排序结束。
为什么选择排序不稳定?
选择排序的进阶版本
双向选择排序,一次排序过程中,同时选出最大值和最小值,放在无需区间的最后和最前。
5.插入排序(O(n^2)) 类似于打牌(稳定)
思路:将集合分为俩个区间,i是当前已经便利的元素,一个已经排序的区间[0...i),一个待排序的区间(i...n]。
每次从待排序区间找到一个元素,放入已排序的区间的合适位置,直到整个数组有序。
在近乎有序的数组下,插入排序的数组性能非常好,甚至优于nlog(n)的排序。
插入排序和选择排序最大的不同在于:当插入排序当前遍历的元素>前驱元素时,此时可以提前结束内层循环。
极端场景下,当集合是一个完全有序的集合,插入排序内层循环一次都不走,插入排序时间复杂度变为O(n)。
插入排序经常用作高阶排序算法的优化手段之一
了解:插入排序优化的手段:(用二分查找法)
折半插入排序:
首先:二分查找的前提是该集合必须是一个有序的集合。
而在插入排序中,我们每次都是在有序区间中找一个合适的插入位置,所以可以使用二分查找法来定位元素的插入位置。
这里注意这个公式为什么要这么写:(是为了防止溢出)
6.希尔排序:缩小增量的排序(不稳定)
先选定一个整数(gap,gap一般都选数组长度的一半或者1/3)
将待排序的数组先按照gap分组,不同组之间使用插入排序,排序之后,再将gap/==2或者gap/=3,重复上述的流程,直到gap=1
当gap=1时,整个数组已经被调整的近乎有序,此时就是插入排序最好的场景,最后再在整个数据上进行一次插入排序。
插入排序和希尔排序,当gap=1时,公式完全一样。
希尔排序为什么要不断地向前比较,能否从零开始向后看gap步?
例:3-----2-----1
不断向后比较
2-----3-----1
2-----1-----3
这样的话,2就没办法换到合适的位置了
7.归并排序(和希尔排序很像)
思想:将原数组不断拆分,一直拆到每个子数组只有一个元素,第一个阶段结束,归而为1这个阶段结束。
并:将相邻的两个数组合并为一个有序的数组,直到整个数据有序。
分:用的是递归
并:第一步:先创建一个大小为合并之后数组大小的临时数组aux,将数组的值拷贝过去。
第二步:如下
为何合并过程需要创建一个新的aux数组?
防止在合并过程中,因为小元素要覆盖大的元素,丢失某些元素
归并排序是一个稳定的nlogN排序算法,此处的稳定指的是时间复杂度稳定(无论集合中的元素如何变化,归并排序的时间复杂度一直都是nlogN,不会退化为O(N^2))且归并排序也是一个稳定性的排序算法(相等元素的先后顺序并不会发生改变)。
(递归类似于树结构,n不断的2/2/2/2,直到只剩下一个元素,这就是logN(树的高度)级别的样子)
递归的深度就是我们拆分数组所用的时间,就是树的高度,logN。
public static void mergeSort(int []arr){ mergeSortInternal(arr,0,arr.length-1); } //在arr[l..r]进行归并排序 private static void mergeSortInternal(int[] arr, int l, int r) { if(l>=r){ //当前数组只剩下一个元素,归过程就结束了 return; } int mid=l+((r-1)>>1); //将原数组拆分为左右俩个小区间,分别递归进行归并排序 mergeSortInternal(arr,l,mid); mergeSortInternal(arr,mid+1,r); //merge操作 merge(arr,l,mid,r); }
而o(n)来自于合并两个子数组的过程merge,就是一个数组的遍历
综上为非常稳定的nlogN。
归并排序的两点优化:
1.当左右两个子区间走完子函数后,左右两个区间已经有序了
如果此时arr[mid]<arr[mid+1]
arr[mid]已经是左区间的最大值
arr[mid+1]已经是右区间的最小值
=>整个区间已经有序了,每必要在执行merge过程,只有左右俩个区间还有先后顺序不同时,才merge。
2.在小区间上,我们可以直接使用插入排序来优化,没必要一直拆分到1位置
r-1<=15,使用插入排序性能很好的。
可以减少归并的递归次数 。