归并排序(重温经典算法系列)

:要怀抱希望哈 :)

    • 原理简述
    • 递归调用(自顶向下,Top-down)
    • 迭代实现(自底向上,Bottom-up)
    • 参考材料






原理简述

单个元素肯定有序;
归并排序采用分治思想,分而治之:
将待排序数组划分为n等分,每份长度为1个元素,则 n份全部有序;
再一生二,二生四,逐步两两元素有序的区间,归一合并成1个有序区间;
最终会归并出整一个数组元素有序的结果.
归并排序有两种实现方式:
(1)自顶向下递归调用实现;
(2)自底向上迭代执行实现.


递归调用(自顶向下,Top-down)

//左右双闭区间[low,high];
//归并过程;
template<typename T>
void __Merge(T* arr, T* tempArr, const int low, const int mid, const int high) {

	//临时存储空间需要在每一次归并之前更新数据,使得已排序的元素得以存储;
	for (int index = low; index <= high; ++index) {
		tempArr[index] = arr[index];
	}

	int i = low;//左区间[low,mid]起点;
	int j = mid + 1;//右区间[mid+1,high]起点;

	//归并过程,使用索引k记录已排好序的最后元素位置;
	for (int k = low; k <= high; ++k) {
		if (i > mid) {//左区间已遍历完;
			arr[k] = tempArr[j++];
		}
		else if (j > high) {//右区间已遍历完;
			arr[k] = tempArr[i++];
		}
		else if (tempArr[i] <= tempArr[j]) {//相等时取左侧元素以保持排序的稳定性;
			arr[k] = tempArr[i++];
		}
		else {//左大右小时,才取右区间元素;
			arr[k] = tempArr[j++];
		}
		////输出并理解中间过程;
		//RandomArrayFuntionHelper::PrintArray(arr, low, high+1);
		//RandomArrayFuntionHelper::PrintArray(tempArr, low, high+1);
		//std::cout << " ------\n";


	}
}
//分治过程;
template<typename T>
void __MergeSort(T* arr, T* tempArr, int low, int high) {//左闭右开区间,[low,high);

	//if (low < high) {
	//通过修改条件并通过else代码,使用直接插入排序优化;
	if (low + 15 < high) {
		//int mid = low + (high - low) / 2;
		int mid = low + ((high - low) >> 1);

		//分(二分、折半);
		__MergeSort(arr, tempArr, low, mid);
		__MergeSort(arr, tempArr, mid + 1, high);
		//治(归并、合一);
		if (arr[mid] > arr[mid + 1]) {
			__Merge(arr, tempArr, low, mid, high);
			//下一行代码为适得其反的优化,https://blog.csdn.net/tinyDolphin/article/details/78457343;
			//__Merge(tempArr, arr, low, mid, high);
		}
	}
	else {
		DirectInsertionSort(arr, low, high);
		//BinaryInsertSort(arr, low, high);
		//经测试,百万数量级时:剩余 10~50 元素左右时使用,效果较佳(治在if优化之前15最佳,加了if条件之后50最好);
		//千万数量级时:剩余 30 元素左右时使用,效果较佳;
		//二分插入的优化比直接插入的优化稍微逊色,也可以适当使剩余元素变为最佳状态时的 3/2倍左右,二分的效果更佳;
	}
}
//递归调用(Top-down);
template<typename T>
//void DataSort::MergeSortTD(T* arr, const std::size_t n) {
void MergeSortTD(T* arr, const std::size_t n) {
	//临时数组空间;
	T *tempArr = new T[n];
	//仅处理元素索引区间(相当于左右全闭区间);
	__MergeSort(arr, tempArr, 0, n - 1);
	delete[] tempArr;
}

温馨提示:
代码中间采用了小规模时,直接通过直接插入排序算法实现优化;
该部分代码链接在我的另外一篇文章里,链接如下:

插入排序(重温经典算法系列)

//重载直接插入排序;
template <typename T>
void DirectInsertionSort(T* arr, const int low, const int high) {//high极可能会传入负数值,故而不可使用无符号数;

	assert(arr);

	//i索引遍历待排序元素;
	for (int i = low + 1; i <= high; ++i) {

		T temp = arr[i];
		int	j;

		for (j = i; j > low && arr[j - 1] > temp; --j) {
			arr[j] = arr[j - 1];//把前边的元素往后挪,用这一操作替换掉每一次的数值交换swap,能减少开销;
		}
		//找到合适插入位置j,第二次循环提前终止;
		if (j != i) {
			arr[j] = temp;
		}
	}
}

关于归并排序算法的具体优化思想,可以参考文章:

归并排序及其优化



迭代实现(自底向上,Bottom-up)


需要使用头文件:

#include 
//归并过程;
template<typename T>
void __Merge(T* arr, T* tempArr, const int low, const int mid, const int high) {

	//临时存储空间需要在每一次归并之前更新数据,使得已排序的元素得以存储;
	for (int index = low; index <= high; ++index) {
		tempArr[index] = arr[index];
	}

	int i = low;//左区间[low,mid]起点;
	int j = mid + 1;//右区间[mid+1,high]起点;

	//归并过程,使用索引k记录已排好序的最后元素位置;
	for (int k = low; k <= high; ++k) {
		if (i > mid) {//左区间已遍历完;
			arr[k] = tempArr[j++];
		}
		else if (j > high) {//右区间已遍历完;
			arr[k] = tempArr[i++];
		}
		else if (tempArr[i] <= tempArr[j]) {//相等时取左侧元素以保持排序的稳定性;
			arr[k] = tempArr[i++];
		}
		else {//左大右小时,才取右区间元素;
			arr[k] = tempArr[j++];
		}
		////输出并理解中间过程;
		//RandomArrayFuntionHelper::PrintArray(arr, low, high+1);
		//RandomArrayFuntionHelper::PrintArray(tempArr, low, high+1);
		//std::cout << " ------\n";


	}
}

//归并排序(自底向上迭代版)(Bottom-up);
template<typename T>
//void DataSort::MergeSortBU(T* arr, const std::size_t n) {
void MergeSortBU(T* arr, const std::size_t n) {

	T* tempArr = new T[n];
	//最开始的小规模数组,使用直接插入排序优化;
	const std::size_t littleSize = 16;
	for (std::size_t index = 0; index < n; index += littleSize) {
		DirectInsertionSort(arr, index, std::min(index + littleSize - 1, n - 1));
	}
	//第 i ( i = 0,1,2 …… )轮要归并已分别各自有序的两个数组范围是;
	//arr[ index + i * sz, index + (i+1) * sz - 1];
	//arr[ index + (i+1) * sz, index + (i+2) * sz - 1];
	for (std::size_t sz = littleSize; sz < n; sz += sz) {//分组长度size(元素个数);
		for (std::size_t index = 0; index + sz < n; index += sz + sz) {
			if (arr[index + sz - 1] > arr[index + sz]) {
				__Merge(arr, tempArr, index, index + sz - 1, std::min(index + sz + sz - 1, n - 1));
			}
		}
	}

	delete[] tempArr;
}


参考材料


归并排序优化思想解说,可以参考:

归并排序及其优化

归并排序算法的动画过程演示,可以参考:
排序算法过程演示

交流方式
QQ —— 2636105163(南国烂柯者)


温馨提示:
转载请注明出处!!


文章最后更新时间: 2020年3月29日23:24:16

你可能感兴趣的:(程序设计进阶·算法设计)