原理
归并排序采用分治的策略,是一个稳定排序算法。其基本思想是:将待排序的序列划分为长度大致相同的两个子序列,对两个子序列分别进行归并排序,即对子序列进行相同规则的划分与排序,直到子序列的长度为1,最终将排好序的子序列合并成排好序的序列。
实现
归并排序具体有两种实现方式:递归的与非递归的,亦称为从上往下的与从下往上的。
递归实现
思路:
1)将长度为n的待排序序列arr[0:n](左闭右开)一分为二,即取序列中点mid = left + (right-left)/2,将序列分为arr[0:mid]与arr[mid:n]两个子序列
2)对两个子序列执行1)中的操作,直到分出的子序列长度为1
3)将划分的两个子序列合并,即得到排好序的序列
- 动态分配临时数组空间
void Merge(int arr[], int left, int mid, int right)
{
int i = left, j = mid, k = 0;
int *tempArr = new int[right-left+1];
while(i < mid || j < right)
{
if(j >= right || (i < mid && arr[i] <= arr[j]))
tempArr[k++] = arr[i++];
else
tempArr[k++] = arr[j++];
}
for(k = left; k < right; ++k)
arr[k] = tempArr[k-left];
delete tempArr;
}
void MergeSort(int arr[], int left, int right)
{
if(left+1 >= right) return;
int mid = left + (right-left)/2;
MergeSort(arr, left, mid);
MergeSort(arr, mid, right);
Merge(arr, left, mid, right);
}
- 静态分配临时数组空间
void _merge(int* a, int* t, int left, int mid, int right) {
int x = left, y = mid, z = left;
while (x < mid || y < right) {
if (y >= right || (x < mid && a[x] <= a[y])) {
t[z++] = a[x++];
} else {
t[z++] = a[y++];
}
}
for (int i = left; i < right; ++i) {
a[i] = t[i];
}
}
void _mergeSort(int* a, int* t, int left, int right) {
if (left >= right-1) {
return;
}
int mid = left + ((right-left)>>1);
_mergeSort(a, t, left, mid);
_mergeSort(a, t, mid, right);
_merge(a, t, left, mid, right);
}
void mergeSort(int* a, int left, int right) {
int t[right-left];
_mergeSort(a, t, left, right);
}
非递归实现
非递归实现与递归实现相反,从长度为1的子序列开始排序,将它们两两合并,然后依次对长度为2、2^2、2^3...的序列进行排序与合并,直到完成所有数的排序。
而非递归实现中,有另一种做法,称为自然归并排序。事实上,对于给定的数组,通常存在多个长度大于1的已自然排好序的子序列段,如{2, 5, 7, 3, 1, 9, 4}中,{2, 5, 7}, {3}, {1, 9}, {4}就是四个已自然排好序的子序列段,可通过O(n)的遍历找出,将相邻的已自然排好序的子序列段两两合并,初次合并后继续两两合并已合并过一次的子序列段,直到整个数组都排好序。
这里实现的是一般的非递归实现
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
void Merge(int arr[], int left, int mid, int right)
{
int i = left, j = mid, k = 0;
int *tempArr = new int[right-left+1];
while(i < mid || j < right)
{
if(j >= right || (i < mid && arr[i] <= arr[j]))
tempArr[k++] = arr[i++];
else
tempArr[k++] = arr[j++];
}
for(k = left; k < right; ++k)
arr[k] = tempArr[k-left];
delete tempArr;
}
void MergeHelp(int arr[], int step, int len)
{
int i = 0;
while(i <= len-(step<<1))
{
Merge(arr, i, i+step, i+(step<<1)); //对arr[i:i+step],arr[i+step,i+2*step]排序
i += (step<<1); //对下一对子序列进行合并
}
if(i+step < len) //如果剩下的数比step还多,就再进行一次Merge
Merge(arr, i, i+step, len);
}
void MergeSort(int arr[], int len)
{
int step = 1;
while(step < len)
{
MergeHelp(arr, step, len);
step <<= 1; //每次增大序列的长度
}
}
int main()
{
int n = 7, a[] = {2, 5, 7, 3, 1, 9, 4};
MergeSort(a, n);
for(int i = 0; i < n; ++i)
cout << a[i] << ' ';
return 0;
}
复杂度分析
归并排序每次将数据规模减半,且每次除去递归所需要做的操作的复杂度显然为O(n),那么有
根据主定理,有T(n) = Ω(nlogn)。