1、核心:有序子列的归并
两个子列一共有N个元素,则归并的时间复杂度:T(N)=O(N)
/* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
void Merge(ElementType A[], ElementType TmpA[],
int L, int R, int RightEnd)
{
int LeftEnd = R - 1; //左边终点位置,假设左右两列挨着
int len = RightEnd - L + 1;
int i = L; //存放结果的数组的初始位置
while (L<=LeftEnd && R<=RightEnd)
{
if (A[L]>A[R])
TmpA[i++] = A[R++];
else
TmpA[i++] = A[L++];
}
while (L <= LeftEnd) //直接复制左边剩下的
TmpA[i++] = A[L++];
while (R <= RightEnd) //直接复制右边剩下的
TmpA[i++] = A[R++];
for (int i = 0; i < len; i++,RightEnd--)
A[RightEnd] = TmpA[RightEnd];
}
2、递归算法
分而治之
void MSort(ElementType A[], ElementType TmpA[], int L, int RightEnd)
{
if(L<RightEnd)
{
int center = (L + RightEnd) / 2;
MSort(A,TmpA,L,center);
MSort(A,TmpA,center+1,RightEnd);
Merge(A,TmpA,L,center+1,RightEnd);
}
}
T(N) = T(N/2) + T(N/2) + O(N)
推倒得出
T(N) = O(N logN)
统一函数接口
void Merge_Sort(ElementType A[], int N)
{
ElementType *TmpA = (int *)malloc(N*sizeof(ElementType));
if (TmpA != NULL)
{
MSort(A, TmpA, 0, N - 1);
free(TmpA);
}
else
cout << "空间不足";
}
问题:为什么在Merge,MSort的变量中声明临时数组
如果只在Merge函数中声明临时数组
void Merge( ElementType A[], int L, int R, int RightEnd)
void MSort( ElementType A[], int L, int RightEnd)
看起来会简洁一点,但有很大的问题。
递归的调用MSort,每一次合并都需要malloc和free,额外的空间复杂度非常大,将临时数组作为Merge和MSort的参数,只需要在Merge_Sort申请一次空间,额外空间复杂度是O( N )
递归算法完整c++代码
#include<iostream>
using namespace std;
typedef int ElementType;
/* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
void Merge(ElementType A[], ElementType TmpA[],
int L, int R, int RightEnd)
{
int LeftEnd = R - 1;
int len = RightEnd - L + 1;
int i = L;
while (L<=LeftEnd && R<=RightEnd)
{
if (A[L]>A[R])
TmpA[i++] = A[R++];
else
TmpA[i++] = A[L++];
}
while (L <= LeftEnd)
TmpA[i++] = A[L++];
while (R <= RightEnd)
TmpA[i++] = A[R++];
for (int i = 0; i < len; i++,RightEnd--)
A[RightEnd] = TmpA[RightEnd];
}
void MSort(ElementType A[], ElementType TmpA[], int L, int RightEnd)
{
if(L<RightEnd)
{
int center = (L + RightEnd) / 2;
MSort(A,TmpA,L,center);
MSort(A,TmpA,center+1,RightEnd);
Merge(A,TmpA,L,center+1,RightEnd);
}
}
void Merge_Sort(ElementType A[], int N)
{
ElementType *TmpA = (int *)malloc(N*sizeof(ElementType));
if (TmpA != NULL)
{
MSort(A, TmpA, 0, N - 1);
free(TmpA);
}
else
cout << "空间不足";
}
int main()
{
int a[] = { 4, 6, 1, 8, 9, 3, 7, 0 };
int len = sizeof(a) / sizeof(a[0]);
Merge_Sort(a, len);
for (int i = 0; i < len; i++)
cout << a[i] << " ";
return 0;
}
3、非递归算法
子序列长度为1归并,再子序列长度为2归并,子序列长度为4归并……
直到子序列长度大于等于数组长度。
先将前面成组的元素进行合并,定义length为子序列的长度,当i <= N-2*length不满足时,剩下的元素没有两个完整的子序列了,如果剩下的元素大于一个子序列,再合并一次,如果小于一个子序列,因为是已经排好序的,直接拷贝过来。这样就完成了子序列长度为length的归并。
void Merge_pass(ElementType A[], ElementType TmpA[],
int N,int length) //length = 当前有序子列的长度
{
int i;
//先将前面成组的元素进行合并,
//当i <= N-2*length不满足时,剩下的元素没有两组了
for (i = 0; i <= N-2*length; i += 2 * length)
Merge(A, TmpA, i, i + length, i + 2 * length-1);
//这里Merge中A元素归并后放入TmpA后,不需要拷贝会数组A了
if (i + length < N) //归并最后2个子列
Merge(A, TmpA, i, i + length, N-1);
else //最后只剩1个子列
for (int j = i; j < N; j++)
TmpA[j] = A[j];
}
子序列长度从1开始,直到大于等于整个数组长度为止。
将A元素归并至TmpA,再将TmpA元素归并至A,每归并一次,子序列长度乘以2。如果某一次将A中元素归并至TmpA中,已经排好序了,子序列长度乘以2,再一次归并是将TmpA中元素拷贝至A中。这样额外的空间复杂度为O(n)。
void Merge_Sort(ElementType A[], int N)
{
int length = 1; //初始化子序列长度
ElementType *TmpA;
TmpA=(int *)malloc(N * sizeof(ElementType));
if (TmpA != NULL)
{
while (length < N)
{
Merge_pass(A, TmpA, N, length);
length *= 2;
//如果已经排好序了,这一步仅仅是从TmpA中将元素拷贝进A中
Merge_pass(TmpA, A, N, length);
length *= 2;
}
free(TmpA);
}
else
cout << "空间不足";
}
非递归算法完整c++代码
#include<iostream>
using namespace std;
typedef int ElementType;
//L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置
void Merge(ElementType A[], ElementType TmpA[],
int L, int R, int RightEnd)
{
int LeftEnd = R - 1;
int len = RightEnd - L + 1;
int i = L; //最左侧元素下标为L,从L开始归并
while (L <= LeftEnd && R <= RightEnd)
{
if (A[L]>A[R])
TmpA[i++] = A[R++];
else
TmpA[i++] = A[L++];
}
while (L <= LeftEnd)
TmpA[i++] = A[L++];
while (R <= RightEnd)
TmpA[i++] = A[R++];
}
void Merge_pass(ElementType A[], ElementType TmpA[],
int N,int length)//length = 当前有序子列的长度
{
int i;
//先将前面成组的元素进行合并,
//当i <= N-2*length不满足时,剩下的元素没有两组了
for (i = 0; i <= N-2*length; i += 2 * length)
Merge(A, TmpA, i, i + length, i + 2 * length-1);
//这里Merge中A元素归并后放入TmpA后,不需要拷贝会数组A了
if (i + length < N) //归并最后2个子列
Merge(A, TmpA, i, i + length, N-1);
else //最后只剩1个子列
for (int j = i; j < N; j++)
TmpA[j] = A[j];
}
void Merge_Sort(ElementType A[], int N)
{
int length = 1; //初始化子序列长度
ElementType *TmpA;
TmpA=(int *)malloc(N * sizeof(ElementType));
if (TmpA != NULL)
{
while (length < N)
{
Merge_pass(A, TmpA, N, length);
length *= 2;
//如果已经排好序了,这一步仅仅是从TmpA中将元素拷贝进A中
Merge_pass(TmpA, A, N, length);
length *= 2;
}
free(TmpA);
}
else
cout << "空间不足";
}
int main()
{
int a[] = { 4, 6, 1, 8, 9, 3, 7, 0 };
int len = sizeof(a) / sizeof(a[0]);
Merge_Sort(a, len);
for (int i = 0; i < len; i++)
cout << a[i] << " ";
return 0;
}
说明:归并排序占用额外O(n)的空间复杂度,一般不用于内排序,在外排序上有很多优点。
内排序:所有的数据都在内存中;
外部排序:大文件的排序,即待排序的记录存储在外存储器上,无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。