归并排序思想-源码
注:本文内容参考左程云算法,需要的可以去自行搜索学习,我只是做学习记录并分享给大家
什么是归并排序呢?
简单的理解就是,给定一个数组。我们可以把他分成两半,先让左半部分有序,再让右半部分有序,最终归并起来,再次排序,那么整个数组就都有序。
我知道看到这里,你可能有些不了解,继续往下看吧。我将分递归版和非递归版来说明
递归版
由于代码比较简单,我们就直接开写,也写了详细的介绍。
// 递归方法实现
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
/**
* 过程就是,先求出 L和R的中点
* 1):先让[L,mid]有序
* 2):再让[mid+1,R]有序
* 3):merge(arr,L,mid,R)就算让,将左右两部分合并起来,让整体有序
*
* 这是一个黑盒过程,具体排序过程在merge方法里面
* @param arr 数组
* @param L 左边界
* @param R 右边界
*/
public static void process(int[] arr, int L, int R) {
if (L == R) { // base case
return;
}
int mid = L + ((R - L) >> 1);
process(arr, L, mid);
process(arr, mid + 1, R);
merge(arr, L, mid, R);
}
/**
* merge过程可以结合代码和下方的图片理解
*
* @param arr 数组
* @param L 左边界
* @param M 中点
* @param R 右边界
*/
private static void merge(int[] arr, int L, int M, int R) {
//定义辅助数组help
int[] help=new int[R-L+1];
/**
* 下面三个变量是指针:
* i-是help的下标索引
* p1-左边界下标索引
* p2-右边界下标索引
*/
int i=0;
int p1=L;
int p2=M+1;
//在p1和p2都合法的范围内,谁小取谁,知道左右边界有一边越界为止
while(p1<=M && p2<=R){
help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
}
//要么p1越界,要么p2越界
while(p1<=M){
help[i++]=arr[p1++];
}
while(p2<=R){
help[i++]=arr[p2++];
}
//将原数组重新赋值
for(i=0;i<help.length;i++){
arr[L+i]=help[i];
}
}
}
- merge过程图解
注意的是,每次merge的时候,左右两边都是已经排好序的了。
非递归版
相信你一定能够很容易理解上面的写法,不过可能你还是不能理解为什么 归并排序的时间复杂度是O(logN*N),接下来我们从非递归版的写法中就可以很容易理解了
- 迭代版的代码思想和递归版的差不多。
递归版是从大到小,一层一层排序完,再往回merge合并排序。
迭代版是根据步长,从小到大,一层一层merge。
现在可以就清楚知道为什么时间复杂度是O(NlogN)了,因为每次merge都是不回退的,那就是O(N);
而步长是 2倍2倍的变化,那就是O(logN),所以合起来就是O(NlogN)了
具体代码如下,有详细的注释
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
int N = arr.length;
// 步长
int mergeSize = 1;
while (mergeSize < N) { // log N
//当前左组的,第一个位置
int L = 0;
//当左组超过,数组长度时,当前轮结束
while (L < N) {
//当发现这组的左边界小于步长时,此轮merge结束
if (mergeSize >= N - L) {
break;
}
//中点
int M = L + mergeSize - 1;
//若右边界小于步长,就以实际长度
int R = M + Math.min(mergeSize, N - M - 1);
//merge
merge(arr, L, M, R);
L = R + 1;
}
// 防止溢出,如果不了解为什么,可以看下面的图
if (mergeSize > N / 2) {
break;
}
mergeSize <<= 1;
}
}
- 步长越界
- 过几天我会写一些过于归并排序实际应用的题目,感兴趣的小伙伴可以关注一下