归并排序
package practice_03_sort;
import java.util.Arrays;
/**
* 归并排序
* 先对左边进行排序,再对右边进行排序,然后将左右两边进行合并成一个统一有序的数组
* 复杂度 O(N*logN)
*/
public class Code01_MergeSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
/**
* 将数组从[L ......R]上进行排序
*
* @param arr 待排序数组
* @param L 起始位置
* @param R 终点位置
*/
private static void process(int[] arr, int L, int R) {
if (L == R) {
return;
}
//获取中点位置
//等价于L+(R-L)/2。 可以预防数据过大时中间递归出现L+R出现溢出的情况
int mid = L + ((R - L) >> 1);
//将一直二分到单个数据。可以打印二分情况
System.out.println("L= " + L + " ,mind= " + mid + " ,R= " + R);
//对左边进行排序
process(arr, L, mid);
//对右边进行排序
process(arr, mid + 1, R);
//对排好序的左右进行merge操作
merge(arr, L, mid, R);
}
/**
* 将两段有序的数据片段合并成一个更大的完全有序的数据列表
* 示例:左右为 1,5 | 4,9
* 合并为:1,4,5,9
* 两个数据段从左至右逐个进行数据比较,完成排序操作
* <p>
* 需要注意 L和R在递归后只会是其中的一段数据,而不再是整个数据的起始和结束位置
*
* @param arr 待排序数组
* @param L 起始位置
* @param M 中间位置
* @param R 终点位置
*/
private static void merge(int[] arr, int L, int M, int R) {
//申请一个中间存放数据用的数组
int[] help = new int[R - L + 1];
int i = 0;
//设置两个左右开始位置的游标来进行数据操作
int p1 = L;
int p2 = M + 1;
//进行数据merge
while (p1 <= M && p2 <= R) {
//i++ 会在执行完语句之后再进行自加操作,如果没有执行就会进行++操作
//两个片段进行比较:谁小取谁的值,并将游标下移一位(未取数的片段不会执行++操作)
help[i++] = (arr[p1] <= arr[p2]) ? arr[p1++] : arr[p2++];
}
//此时左边或者右边可能还存在有残余数据,此时的数据为有序数据 直接逐一添加的末尾即可
//以下两个while只会执行其中一个
while (p1 <= M) {
help[i++] = arr[p1++];
}
while (p2 <= R) {
help[i++] = arr[p2++];
}
//将排序好的数据放回到特定的其实结束段中
//System.arraycopy(help, 0, arr, L + 0, help.length);
//IntStream.range(0, help.length).forEach(j -> arr[L + j] = help[j]);
for (int j = 0; j < help.length; j++) {
arr[L + j] = help[j];
}
}
public static void main(String[] args) {
//简单测试
int[] arr = {0, 89, 3, 5, 63, 9, 7};
System.out.println(Arrays.toString(arr));
mergeSort(arr);
System.out.println(Arrays.toString(arr));
System.out.println("-------------------");
//对数器测试
}
}
简单测试结果:
-------------------
[0, 89, 3, 5, 63, 9, 7]
L= 0 ,mind= 3 ,R= 6
L= 0 ,mind= 1 ,R= 3
L= 0 ,mind= 0 ,R= 1//数据会逐一切分到单个数据
L= 2 ,mind= 2 ,R= 3
L= 4 ,mind= 5 ,R= 6
L= 4 ,mind= 4 ,R= 5
[0, 3, 5, 7, 9, 63, 89]
-------------------
结论:归并排序利用了左右段合并时数据的有序性。提前实现了数据的单调性,从而使得在merge时能不进行数据回退,从而不浪费数据之间的比较。实现了O(N*logN)速度