归并排序的原理并不是很复杂,他的意思是说,将一个数组从中间分解成左右两个部分,然后对左右两部分分别排序,将排序后的结果再进行合并。
我们先来讨论经典的递归实现方法。
归并排序的递归实现可以总结为:左边排好序+右边排好序+merge让总体有序
这个问题最大的困难就是如何进行合并merge,我们通过画递归树可以知道,最后一定是一个元素和一个元素进行的merge,那么这个merge的过程还需要涉及到对这两个元素进行比较,那么如何去实现呢?
这里我们采用双指针法和辅助数组的方式,首先创建一个辅助数组,左右两个元素分别创建辅助指针,如果左边的元素小,先进入数组,后移,右边的小右边的先进,后移,同样大小,左边的进去,同时后移。
归并排序的复杂度是O(NlogN)
merge过程两个指针都没有回退, 是O(N)
比较行为每一次变成结果再传递, 没有浪费(相比N^2的算法)
为什么比O(N^2)的排序好,最本质的原因是所有O(N^2)的排序,大量浪费比较行为比较行为都是独立的. 上一次发生的比较行为,丝毫不能够加速底下的比较行,大量浪费比较行为
与 插入, 冒泡, 选择等N^2排序的比较
选择等每次排序相对独立,大量浪费比较行为
把有序部分规定/留下来, 没有浪费比较行为
而归并排序 我左组内部的比较行为浪费了吗?没浪费, 都变成了排序好的结果,右侧部分也变成了排序好的结果。当你左侧跟右侧Merge的过程中,左组数之间是根本没有比较的,要比较是左组某一个指针对右组某一个指针在比较,你们此时比较行为也没有浪费, 变成了你们共同的一个排好序的结果,所以比较行为每一次都变成结果在传递,所以复杂度是O(N*logN)
额外空间复杂度为O(N)
现在看看代码
class MergeSort{
public static void mergeSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
process(arr,0,arr,length - 1);
}
public static void process(int[] arr,int left,int right){
if(right == left){
return ;
}
int mid = left + ((right - left) >> 1);
process(arr,left,mid);
process(arr,mid+1,right);
merge(arr,left,mid,right);
}
public static void merge(int[] arr,int left,int mid,int right){
int[] help = new int[right - left + 1];
int p1 = left;
int p2 = mid + 1;
int i = 0;//用于记录数组位置
while(p1 <= mid && p2 <= right){
help[i++] = arr[p1] >= a