归并排序算法的优化——学习笔记

归并排序是一种稳定排序,时间复杂度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较大时应选用二分插排。

插入排序和二分插入排序就不奉上代码了,同学们可以练练手自己实现一下。

猜你喜欢

转载自blog.csdn.net/jjwwwww/article/details/82154408