当要求解的一个输入规模为n且取值又相当大的问题是,直接求解往往比较困难,有的甚至根本没法直接求出。正确的方法是,每当遇到这类问题时,首先应仔细分析问题本身所具有的特性,然后根据这些特性选择适当的设计策略来求解。在将这n个输入分成k个不同子集合的情况下,如果能得到k个不同的可独立求解的子问题(具有重复子问题的用动态规划求解),其中1<k≤n,而且在求出了这些子问题的解之后,还可以找到合适的方法把它们合并成整个问题的解,那么,可考虑使用分治法来求解。如果子问题相对来说还太大,则可反复使用分治策略将这些子问题分成更小的同类型的子问题。
1. 步骤
在分治策略中,我们递归地求解一个问题,在每层中应用如下三步:
(1) 分解:将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小;
(2) 解决:递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解;
(3) 合并:将子问题的解组合成原问题的解。
2. 最大子数组问题
给定一个由正负整数组成的顺序表,求解一组连续的数使其和最大。例如给定一组数据-1,2,3,-2,5,连续数s = {2,3,-2, 5}取得最大值8,其它的连续数之和均小于8,则s为我们要的连续数。
设A[i, j]表示从i开始到j结束的顺序表,m[i, j]表示从第i个数到第j个数中连续数的最大和。
2.1 方法一:暴力破解
计算每一种情况的和,然后记录取得最大值时的连续数。该方法共检测n(n+1)/2种情况,因此时间复杂度为O(n2)。
2.2 方法二:分治
(1) 分解:将问题A[i, j]从中间划分开,分成左右两部分:A[i, middle]和A[middle, j],对应的最大值分别为A[i, middle]和A[middle, j],其中middle=(i+j)/2;
(2) 解决:若问题的规模j-i为0时,直接返回第i个数,即当前规模的最大值;
(3) 合并:A[i, j]取得的最大值m[i, j]为左侧m[i, middle]、右侧m[middle, j]对应的最大值和包含中间值的最大值中的最大值。
运行时间T(n)的递归式为:
求解递归式可得T(n) = O(nlogn),优于暴力破解方法的O(n2)。
3. 递归式求解
递归式的求解共有三种方法:代入法、递归树和主方法。
3.1 代入法
代入法共分为两步:
(1) 猜测解的形式;
(2) 用数学归纳法求出解中的常数,并证明解是正确的。
例如:T(n) = 2T(n/2)+n
(1) 猜测T(n) ≤ cnlgn;
(2) 证明:令m=n/2,有T(n/2) ≤ cn/2lg(n/2);
则:
T(n) ≤ 2(cn/2lg(n/2))+n
≤ cnlg(n/2) + n
= cnlgn – cnlg2 + n
= cnlgn – cn + n
≤ cnlgn
3.2 递归树
例T(n) = 3T(n/4) + O(n2),其递归树如图3-1所示。图中结点中的数字表示合并问题解的代价,因此该递归式的解为图中所有结点中数字之和。易得该递归树最多有log4n+1层,如图中左侧所示;另外图右侧表明了每层的数字之和,则递归式T(n) = 3T(n/4) + O(n2)的解为T(n) = Ο(n2),详细结算过程如下:
图3-1 T(n) =3T(n/4) + O(n2)的递归树
3.2 主方法(The master method)
参考:算法导论