1. 归并排序原理介绍
归并排序是建立在归并操作上的一种排序算法,它是一种稳定排序算法,它的时间复杂度是O(nlogn)。
归并排序算法的核心在于分治(divide-and-conquer),分治法的思想:“分”是将一个问题分解成很多不同的小问题进行递归求解,“治”是将各个小问题的解重新凑合在一起组成最终的答案。
对序列[3, 5, 2, 1, 4, 6, 8, 7]进行归并排序,首先是“分”,分的过程可以看作是递归的过程,将一个序列不停地拆分成越来越小的子序列,每次分割子序列的位置都是数组长度的1/2,递归的深度为,针对下图序列的递归深度为3。
再来看“治”的部分,每一次“治”都是将两个有序的子序列合并成一个新的有序子序列。
以下图为例,子序列[3, 5]和子序列[1, 2]合并成一个新的子序列[1, 2, 3, 5];子序列[4, 6]和[7, 8]合并成为一个新的子序列[4, 6, 7, 8];子序列[1, 2, 3, 5]和子序列[4, 6, 7, 8]最终合并为一个新的子序列[1, 2, 3, 4, 5, 6, 7, 8],此时排序结束。
2. 代码实现
利用一个长度和原数组相同的temp数组,每次对两个子序列进行合并后的结果都存放到temp数组中,然后将temp数组中的有效元素拷贝到原数组中,这样可以保证在排序过程中只操作2个数组,不会产生额外的空间开销。如果不太明白可以参考下图:
/**
* @author Zeng
* @date 2020/3/1 10:35
*/
public class 归并排序 {
public static void merge(int[] arr, int begin, int mid, int end, int[] temp) {
System.out.print("begin:"+begin+";mid:"+mid+";end:"+end+";");
int leftPtr = begin;
int rightPtr = mid + 1;
int tempPtr = 0;
//左右两个子序列的元素由小到大放入temp数组中
while (leftPtr <= mid && rightPtr <= end){
if(arr[leftPtr] < arr[rightPtr]){
temp[tempPtr++] = arr[leftPtr++];
}else{
temp[tempPtr++] = arr[rightPtr++];
}
}
//将左边剩余的元素放入到temp数组中
while (leftPtr <= mid){
temp[tempPtr++] = arr[leftPtr++];
}
//将右边剩余的元素放入到temp数组中
while (rightPtr <= end){
temp[tempPtr++] = arr[rightPtr++];
}
//将temp数组中已经排好序的元素拷贝到arr数组中
tempPtr = 0;
while (begin <= end){
arr[begin++] = temp[tempPtr++];
}
System.out.println("排序结果:"+Arrays.toString(arr));
}
public static void sort(int[] arr, int begin, int end, int[] temp){
//分隔子数组
if(begin < end){
//获取分隔的下标
int mid = (begin + end) >> 1;
//对左边进行归并排序
sort(arr, begin, mid, temp);
//对右边进行归并排序
sort(arr, mid + 1, end, temp);
//左右两边进行合并
merge(arr, begin, mid, end, temp);
}
}
public static void mergeSort(int[] arr){
//创建一个长度相同的暂存数组,避免频繁地创建子数组
int[] temp = new int[arr.length];
sort(arr, 0, arr.length - 1, temp);
System.out.println(Arrays.toString(temp));
}
public static void main(String[] args) {
int[] arr = new int[]{3, 5, 2, 1, 4, 6, 8, 7};
mergeSort(arr);
}
}
//begin:0;mid:0;end:1;排序结果:[3, 5, 2, 1, 4, 6, 8, 7]
//begin:2;mid:2;end:3;排序结果:[3, 5, 1, 2, 4, 6, 8, 7]
//begin:0;mid:1;end:3;排序结果:[1, 2, 3, 5, 4, 6, 8, 7]
//begin:4;mid:4;end:5;排序结果:[1, 2, 3, 5, 4, 6, 8, 7]
//begin:6;mid:6;end:7;排序结果:[1, 2, 3, 5, 4, 6, 7, 8]
//begin:4;mid:5;end:7;排序结果:[1, 2, 3, 5, 4, 6, 7, 8]
//begin:0;mid:3;end:7;排序结果:[1, 2, 3, 4, 5, 6, 7, 8]
//[1, 2, 3, 4, 5, 6, 7, 8]