数组排序任务可以如下完成:
- 把前一半排序
- 把后一半排序
- 把两半归并到一个新的有序数组,然后再拷贝回原数组,排序完成
将两个排序好的数组归并过程如下:
红色的 1 是左边起始位置;
绿色的 2 是右边起始位置;
灰色的 最右边 是右边终点位置;
代码:
#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;
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) //要排序的部分为 L 到 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) //用堆申请内存排序完再 free 就不会浪费栈内存了
{
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;
}
我学到的:
通过使用分治算法的思想来对数组进行排序(这里叫做归并排序),分治算法的核心思想就是把一个问题分解n个小问题,然后把这n个小问题分别解决,最后再把这n个小问题的结果合并便可以得到结果了。(分解–解决–合并)
此题思路是:
先将一个未排列的数组二分、二分、再二分,当分到只有一个元素时再归并,归并就是将原来二分的两个数组合并成一个,归并的时候顺便排序(历遍两个数组排到一个数组上),重复此操作,二分多少次就归并多少次,最后得到的就是一个排好序数组
对n个元素进行排序的时间复杂度:
T(n) = 2 * T(n/2) + a * n (a是常数,具体多少不重要)
= 2 * (2 * T(n/4) + a * n/2)+a * n
= 4 * T(n/4) + 2a * n
= 4 * (2 * T(n/8) + a * n/4)+2 * a * n
= 8 * T(n/8) + 3 * a * n
……
= 2k * T(n/2k) + k * a * n
一直到n/2k = 1 (此时k = log2n)
T(n)= 2k * T(1) + k * a * n
= 2k + k * a * n
= n + a * (log2n) * n
故复杂度为 O(n * logn)