归并排序
归并排序就是利用分而治之的思想,将大规模的问题分解成若干小规模的子问题,当分到规模大小为1的时候,子问题是有序的,这个时候归并已经排好序的子问题,不断归并,直至整个大规模的问题都已经排好序。
举个例子
有一个数组: 3 2 6 8 1
第一次切分: 3 2 6 8 1
第二次切分: 3 2 6 8 1
第三次切分: 3 2 6 8 1
第一次归并: 3 2 6 1 8
第二次归并: 2 3 1 6 8
第三次归并: 1 2 3 6 8
代码实现
public class MergeSort {
/**
* 归并排序:利用了分治思想,将大规模问题分解为一个个小规模的问题,小规模与大规模的解法相似
* 利用递归可以很好的解决分治问题,以下用二路归并解决问题
*/
public static void mergeSort(int[] arr, int start, int end) {
//递归的关键在于处理好边界问题,当要处理的子问题规模小于等于1的时候,子问题就已经解决了
if (end - start > 0) {
//否则将问题一分为二,递归执行子问题
int middle = (start + end) / 2;
mergeSort(arr, start, middle);
mergeSort(arr, middle + 1, end);
//子问题执行完毕需要进行归并
merge(arr, start, end, middle);
}
}
//合并两个子结果
private static void merge(int[] arr, int start, int end, int middle) {
int mergeLen = end - start + 1;
int[] merged = new int[mergeLen];
//arr的start到middle和middle+1到end都是有序的
int i = start, j = middle + 1, k = 0;
//先取两部分的公共部分
while (i <= middle && j <= end) {
if (arr[i] > arr[j]) {
merged[k++] = arr[j++];
} else {
merged[k++] = arr[i++];
}
}
//针对过长的一部分做处理
while (i <= middle) {
merged[k++] = arr[i++];
}
while (j <= end) {
merged[k++] = arr[j++];
}
//修改的部分复制回原数组
System.arraycopy(merged, 0, arr, start, mergeLen);
}
public static void main(String[] args) {
Random r = new Random();
int[] arr = new int[20];
for (int i = 0; i < arr.length; i++) {
arr[i] = r.nextInt(100);
}
//未排序
System.out.printf("排序前:%s \n", Arrays.stream(arr).boxed().map(Object::toString).collect(Collectors.joining(" ")));
//排好序
mergeSort(arr, 0, arr.length - 1);
System.out.printf("排序后:%s \n", Arrays.stream(arr).boxed().map(Object::toString).collect(Collectors.joining(" ")));
}
}
运行结果
排序前:60 50 68 74 63 88 38 68 92 81 27 11 52 65 10 91 21 45 84 81
排序后:10 11 21 27 38 45 50 52 60 63 65 68 68 74 81 81 84 88 91 92