1. 归并排序基本思想
归并排序其实要做两件事:
(1)“分治策略”——将序列每次折半划分,递归分治。
递归的将数组的前半部分和后半部分数据各自归并排序,得到排序后的两部分数据,然后使用下面的合并算法再将这两部分合并到一起。
(2)“合并算法”——将划分后的序列段两两合并后排序。
基本的合并算法是取两个输入数组A和B,一个输出数组C,以及三个计数器Aptr,Bptr和Cptr它们的初始位置对应于数组的开始端。A[Aptr]和B[Bptr]中的较小者被拷贝到C中的下一个位置,相关计数器向前推进一步。当前输入表有一个用完的时候,则将另一个表中剩余部分拷贝到C中。
2. 归并排序完整代码
#include <iostream>
using namespace std;
// 分治,递归
template<class T>
void MSort(T A[], T TempArr[], int left, int right){
int center;
if (left < right){//////
center = (left + right) / 2;
MSort(A, TempArr, left, center);
MSort(A, TempArr, center + 1, right);
Merge(A, TempArr, left, center+1, right);
}
}
// 合并
template<class T>
void Merge(T A[], T TempArr[], int Lpos, int Rpos, int Rend){
int len = Rend - Lpos + 1;
int Lend = Rpos - 1;
int TempPos = Lpos;
// 当两个字部分的元素都没有比较完
while (Lpos <= Lend && Rpos <= Rend ){
A[Lpos] <= A[Rpos] ? TempArr[TempPos++] = A[Lpos++] : TempArr[TempPos++] = A[Rpos++];
}
// 其中一部分的元素用完
// 把左边部分剩余元素拷贝到TempArr中
while (Lpos <= Lend){
TempArr[TempPos++] = A[Lpos++];
}
while (Rpos <= Rend){
TempArr[TempPos++] = A[Rpos++];
}
// 把临时数组TempArr中元素拷贝到A数组中
for (int i = 0; i < len; ++i){
A[i] = TempArr[i];
}
/* ERROR */
//for (int i = len; i >= 0; --i){
// A[i] = TempArr[i];
//}
/* OK */
//for (int i = 0; i < len; ++i, --Rend){
// A[Rend] = TempArr[Rend];
//}
}
template<class T>
void MergeSort(T A[], int n){
T *TempArr = new T[n];
if (TempArr != NULL){
MSort(A,TempArr, 0, n-1);
delete[] TempArr;
}else
cout << "No space for temp array!";
}
void main(){
int A[] = { 1, 13, 24, 26, 2, 15, 27, 38 };
int n = 8;
MergeSort(A, 8);
cout << "归并排序后的元素:";
for (int i = 0; i < n; ++i){
cout << A[i] << " ";
}
cin.get();
}
3.归并排序时间复杂度
- 时间复杂度:最坏情况、平均情况和最好情况都是O(NlogN),该算法为稳定的算法;
- 空间复杂度:O(N),在算法处理过程中,需要一个大小为n的临时存储空间用以保存合并序列;
- 归并排序和堆排序、快速排序的比较
空间复杂度:堆排序 < 快速排序 < 归并排序;
稳定性:堆排序和快速排序都是不稳定;
从平均情况下的排序速度角度出发,应该选择快速排序。