采用分治的思想(将大问题拆除若干个小问题再解决),我们知道单个元素时一定有序,所以我们可以先将数组拆成一个个有序数组(单元素)然后在对这些有序数组合并(使用双指针时间复杂度仅为O(n)),这样就大幅提高了排序的效率。
最重要的是归并排序十分稳定,无论是什么排列的数组我们拆分的时间开销都是O(logn),合并是O(n),所以总的时间复杂度都为O(nlogn),和其它排序算法(桶排序、快排等)相比受待排数组影响小,适合未知数组情况且总量大的情况。
归并排序的讲解见代码部分会结合代码讲解。
public class Main {
public static void main(String[] args) {
int[] nums = {2,31,5,2,1,6};
new Main().mergeSort(nums, new int[nums.length], 0, nums.length - 1);
System.out.println(Arrays.toString(nums));
}
//拆分
public void mergeSort(int[] nums, int[] tempArr, int left, int right) {
if(right - left <= 0) return;//当没有元素或只有一个元素时不可再分就返回
int mid = left + (right - left) / 2;
mergeSort(nums, tempArr, left, mid);//分左边的数组
mergeSort(nums, tempArr, mid + 1, right);//分右边
merge(nums, tempArr, left, right, mid);//合并操作
}
//合并
private void merge(int[] nums, int[] tempArr, int left, int right, int mid) {
int i = left, j = mid + 1;//被分出来的两部分数组的首下标
int k = left;//我们要操作的tempArr数组的开始位置
while(i <= mid && j <= right) {
if(nums[i] <= nums[j]) {//左边部分小,把小的数放入tempArr
tempArr[k++] = nums[i++];
} else {//右边小
tempArr[k++] = nums[j++];
}
}
//处理剩余元素
while(i <= mid) {
tempArr[k++] = nums[i++];
}
while(j <= right) {
tempArr[k++] = nums[j++];
}
//将tempArr的变化更新到原数组nums
for(int index = left; index <= right; index++) {
nums[index] = tempArr[index];
}
}
}
具体的过程是这样的:
2,31,5,2,1,6
拆分为:
2,31,5 2,1,62,31 5 2,1 6 同一颜色代表是从同一集合拆分出的
2 31 5 2 1 6
合并:合并{2}{31} {2,31} {5} 合并{1}{2} {1,2} {6}
合并{2,5}{31} {2,5,31} 合并{1,2}{6} {1,2,6}
合并{2,5,31}{1,2,6} {1,2,2,5,6,31}