这一章我们讲解分治算法。
1、分治的基本概念
把一个任务,分成形式和原任务相同,但规模更小的几个部分任务(通常是两个部分),分别完成,或只需要选一部完成。然后再处理完成后的这一个或几部分的结果,实现整个任务的完成。
分治实例 – 称假币
16枚硬币,有可能有1枚假币,假币比真币轻。有一架天 平,用最少称量次数确定有没假币,若有的话,假币是哪一枚。
8 – 8 一称,发现无假币,或假币所在的那8枚
4 – 4 一称
2 – 2 一称
1 – 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 + 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;
}
归并排序的时间复杂度
对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(nlogn)