1. 分治策略的基本思想
1.1例子
二分查找:在一个排好序的数组T[1..n]中查找x,如果x在T中,输出x在T中的下标j;如果x不在T中,输出j=0。使用下述二分查找算法。
算法1.1 BinarySearch(T,l,r,x)
输入:数组T,下标从l到r;数x
输出:j
l←1;r←n
while l≤r do
m←(l+r)/2
if T[m]=x then return x
else if T[m]>x then r←m-1;
else l←m+1
return 0
通过x与数组T中元素的1次比较,T中需要检索的范围至少减半,因此检索次数W(n)满足递推方程:W(n)=W(n/2)+1,w(1)=1;解出W(n)=logn+1
二分归并排序:将被排序的数组分成两个相等的子数组,然后使用同样的方法对子数组排序,最后将两个排好序的子数组归并成一个数组。
算法1.2 MergeSort(A,p,r)
输入:数组A[p..r]
输出:按照递增顺序排好的数组A
if p<r then
q←(p+r)/2;
MergeSort(A,p,q)
MergeSort(A,q+1,r)
Merge(A,p,q,r)
算法1.3 Merge(A,p,q,r)
输入:按照递增顺序排好序的数组A[p..q]与A[q+1,r]
输出:将A[p..q]复制到B[1..x],将A[q+1,r]复制到C[1..y]
i←1,j←1,k←p
while i≤x and j≤y do
if B[i]≤C[j]
A[k]←B[j]
i←i+1;
k←k+1
if i>x then 将C[j..y]复制到A[k..r]
else 将B[i..x]复制到A[k..r]
W(n)满足递归方程:W(n)=2W(n/2)+n-1,W(1)=0,解得W(n)=O(nlogn)
上面两个算法就是用分治策略设计的算法。它们的共同特点是:将规模为n的原问题归约成规模减半的子问题(可以是一个字问题,也可以是多个字问题)。分别求解每个字问题,然后把字问题的解进行综合,从而得到原问题的解。
2.分治算法的一般性描述
把上面的思想加以概括,可以得到分治算法的一般描述。高P是待求解的问题,|P|代表该问题的输入规模,一般的分治算法Divide-and-Conquer的伪码描述如下:
算法2.1 Divide-and-Conquer (P)
if |P|≤c then S(P)
divide P into P1,P2,…,Pk
for i=1 to k do
yi=Divide-and-Conquer (Pi)
return Merge(y1,y2,…,yk)
上述伪码说明:如果问题的规模不超过c(在上述二分检索和二分归并算法中c=1),算法停止递归,直接求解P,S(P)就代表直接求解的过程;否则,将P归约成k个彼此独立的子问题P1,P2,…,Pk,然后递归地依次求解这此子问题,得到解y1,y2,…,yk,最后将这k个解归并得到原问题的解,Merge代表归并子问题的解的过程。
分治算法通常是递归算法,这种算法的时间复杂度分析通常需要求解递推方程。如果原问题的输入规模是n,根据上面的伪码,分治算法时间复杂度的递推方程和一般形式是:
W(n)=W(|P1|)+W(|P2|)+…+W(|Pk|)+f(n),W(c)=C;.
上面的C代表直接求解规模为c的子问题的工作量,而f(n)代表将原问题归约为若干个子问题以及将子问题的解综合为原问题的解所需要的工作量。
3. 分治算法的分析技术
在分治算法中常见的递推方程有下面两类:
如果归约后的子问题的规模比原问题呈现常数量级的减少,就会得到第一类递推方程。第一类方程可以用迭代、换元、递归树、尝试等方法求解。
第二类递推方程反映的是类似于二分检索和二分归并排序算法的分治算法,在均衡划分的情况下,a 代表归约后的子问题个数,b代表子问题规模减少的倍数,d(n)表示归约过程和综合解过程的总工作量。这两类方程的求解算法可以使用迭代法、递归树和主定理等,使用大O记号表示解,有如下几种常见的方式:
当d(n)为常数时,如果a=1,那么符合主定理的第二种情况,于是T(n)=O(logn);如果a≠1,不是常数,属于主定理第一种情况,T(n)=O()。
当d(n)=cn时,如果a>b,对应主定理第一种情况,方程的解是=O();如果a=b,对就主定理第二种情况,方程的解是O(nlogn);如果a<b,对应主定理的第三种情况,方程的解是O(f(n))=O(n)。
利用上述结果,直接可以得到二分检索算法的时间复杂度为O(logn),二分归并排序算法的时间复杂度为O(nlogn)。