归并排序是一种稳定排序,时间复杂度O(nlogn),空间复杂度O(n)。
那么已经非常高效的归并排序是否还能再优化呢?当然是可以的,timsort就是在归并排序上改进的一种高级排序方式,现在广泛运用在如python,Java等主流语言中。timsort是高效的,完整的算法是相当复杂的,因此我这里主要根据timsort提两个优化思路。
先附上归并排序的算法:
//num -- 待排序的数组
//start -- 排序的起点
//end -- 排序数组最后一位
void MergeSort(int *nums, int start, int end) {
//判断起点是否小于终点
if (end-start<2) {
return;
}
//进行分割操作
int mid = (start + end) / 2;
MergeSort(nums, start, mid);
MergeSort(nums, mid , end);
//归并两个子列
Merge(nums, start, mid, end);
}
1、避免无用的归并
看例子:
{1,2} {7,8}
当两个子序列已经排好序的状态时,我们就不必再做无用的合并操作,此时加一句判断即可,如下:
void MergeSort(int *nums, int start, int end) {
if (end-start<2) {
return;
}
int mid = (start + end) / 2;
MergeSort(nums, start, mid);
MergeSort(nums, mid , end);
//判断两个子列是否已经排好序
if (nums[mid] < nums[mid - 1]) {
Merge(nums, start, mid, end);
}
}
2、短序列用插入排序
归并排序每次都要递归到长度为1的子序列,当进行大量数据排序的时候,每次递归都是要占用系统栈空间的。为了避免过度调用栈空间,可以设置一个最小归并长度L,当子序列的长度小于L时,我们就调用其他排序,如插入排序,插入排序在小数组的排序效率上要比归并排序稍好。
void MergeSort(int *nums, int start, int end) {
//当长度小于L时改用插入排序
if (end-start<L) {
InsertSort(nums,start,end);
return;
}
int mid = (start + end) / 2;
MergeSort(nums, start, mid);
MergeSort(nums, mid , end);
if (nums[mid] < nums[mid - 1]) {
Merge(nums, start, mid, end);
}
}
3、插入排序用二分插入排序效率更高
二分插入排序是基于二分查找改进的插入排序,效率要更优于插入排序。在数组非常短的时候二分插排和普通插排效率接近,而当数组数量增加后二分插排要明显优于普通插排。
当然这就要看L的取值了。当L取值较小时,运用普通插排即可,而L较大时应选用二分插排。
插入排序和二分插入排序就不奉上代码了,同学们可以练练手自己实现一下。