建议先看排序综述,传送门:数据结构与算法系列之一:八大排序综述。
归并排序(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为 O(nlogn) 。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。
递归法(Bottom-up)
原理如下(假设序列共有 n 个元素):
迭代法(Top-down)
wikipedia的大数据规模演示:
wordzzzz的小数据规模演示:
/*
* 归并排序递归版
*/
template <typename T>
void Merge(T *array, T *reg, int left, int mid, int right) {
int left1 = left, right1 = mid;
int left2 = mid + 1, right2 = right;
int k = left;
while (left1 <= right1 && left2 <= right2)
reg[k++] = array[left1] < array[left2] ? array[left1++] : array[left2++];
while (left1 <= right1)
reg[k++] = array[left1++];
while (left2 <= right2)
reg[k++] = array[left2++];
for (k = left; k <= right; k++)
array[k] = reg[k];
}
template <typename T>
void MergeSortRecursive(T *array, T *reg, int left, int right) {
if (left >= right)
return;
int mid = left + ((right - left) >> 1);
MergeSortRecursive(array, reg, left, mid); //左序列排序
MergeSortRecursive(array, reg, mid + 1, right); //右序列排序
Merge(array, reg, left, mid, right); //合并左右序列
}
template <typename T>
void MergeSort(T *array, const int length) {
if (array == NULL)
throw invalid_argument("Array must not be empty");
if (length <= 0)
return;
T* reg = (T*)malloc(sizeof(T) * length);
if (reg == NULL)
{
fputs("Error: out of memory\n", stderr);
abort();
}
MergeSortRecursive(array, reg, 0, length - 1);
delete[] reg;
}
/*
* 归并排序迭代版
*/
template<typename T>
void MergeSortIteration(T *array, const int length) {
if (array == NULL)
throw invalid_argument("Array must not be empty");
if (length <= 0)
return;
T* regB = (T*)malloc(sizeof(T) * length);
if (regB == NULL)
{
fputs("Error: out of memory\n", stderr);
abort();
}
for (int seg = 1; seg < length; seg += seg) { //步长,每次翻倍
for (int left = 0; left < length; left += seg + seg) {
int low = left, mid = min(left + seg, length), high = min(left + seg + seg, length);//因为可能会超出length
int k = low;
int left1 = low, right1 = mid;
int left2 = mid, right2 = high;
while (left1 < right1 && left2 < right2) //这里的表达式没有等号,都是左闭右开区间
regB[k++] = array[left1] < array[left2] ? array[left1++] : array[left2++];
while (left1 < right1)
regB[k++] = array[left1++];
while (left2 < right2)
regB[k++] = array[left2++];
}
for (int i = 0; i < length; i++) //更新array
array[i] = regB[i];
}
delete[] regB;
}
比较操作的次数介于 (nlogn)/2 和 nlogn−n+1 。 赋值操作的次数是 (2nlogn) 。归并算法的空间复杂度为: Θ(n) 。
对于归并排序有几点说明:
归并排序有以下几点优化方法:
/*
* 归并排序递归版合并函数
*/
template <typename T>
void Merge(T *array, T *reg, int left, int mid, int right) {
int left1 = left, right1 = mid;
int left2 = mid + 1, right2 = right;
int k = left;
while (left1 <= right1 && left2 <= right2)
reg[k++] = array[left1] < array[left2] ? array[left1++] : array[left2++];
while (left1 <= right1)
reg[k++] = array[left1++];
while (left2 <= right2)
reg[k++] = array[left2++];
for (k = left; k <= right; k++)
array[k] = reg[k];
}
/*
* 归并排序递归版递归函数优化
*/
template <typename T>
void MergeSortRecursive1(T *array, T *reg, int left, int right) {
if (left >= right)
return;
if (right - left <= M) //序列长度小于阈值就采用插入排序
InsertSort(array, left, right);
else{
int mid = left + ((right - left) >> 1);
MergeSortRecursive1(array, reg, left, mid); //左序列排序
MergeSortRecursive1(array, reg, mid + 1, right); //右序列排序
Merge(array, reg, left, mid, right); //合并左右序列
}
}
template <typename T>
void MergeSort(T *array, const int length) {
if (array == NULL)
throw invalid_argument("Array must not be empty");
if (length <= 0)
return;
T* reg = (T*)malloc(sizeof(T) * length);
if (reg == NULL)
{
fputs("Error: out of memory\n", stderr);
abort();
}
MergeSortRecursive1(array, reg, 0, length - 1);
delete[] reg;
}
/*
* 归并排序迭代版优化
*/
template<typename T>
void MergeSortIteration1(T *array, const int length) {
if (array == NULL)
throw invalid_argument("Array must not be empty");
if (length <= 0)
return;
T* regA = array;
T* regB = (T*)malloc(sizeof(T) * length);
if (regB == NULL)
{
fputs("Error: out of memory\n", stderr);
abort();
}
for (int seg = 1; seg < length; seg += seg) { //步长,每次翻倍
for (int left = 0; left < length; left += seg + seg) {
int low = left, mid = min(left + seg, length), high = min(left + seg + seg, length);//因为可能会超出length
int k = low;
int left1 = low, right1 = mid;
int left2 = mid, right2 = high;
while (left1 < right1 && left2 < right2) //这里的表达式没有等号,都是左闭右开区间
regB[k++] = regA[left1] < regA[left2] ? regA[left1++] : regA[left2++];
while (left1 < right1)
regB[k++] = regA[left1++];
while (left2 < right2)
regB[k++] = regA[left2++];
}
T* temp = regA; //优化:交换辅助数组与原始数组的角色
regA = regB;
regB = temp;
}
if (regA != array) { //如果regA != array,则说明现在regA是辅助数组
for (int i = 0; i < length; i++) //所以需要拷贝数据到regB,也就是array。
regB[i] = regA[i];
regB = regA; //regB重新指向辅助数组
}
delete[] regB;
}
系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~
完的汪(∪。∪)。。。zzz