分治法与递归调用的不同之处在于分解问题的方式:一般的递归调用总是把问题分解为一个小问题和剩下的所有问题,而分治法会把问题分解为同等大小的子问题。时间复杂度比递归少
分治法具有以下3种构成要素:
- 把问题分解为更小问题的分解过程。
- 把各个小问题的答案合并为原问题答案的合并过程。
- 不需要再分解而直接能够解答的最基本的问题。
示例:数列快速求和与矩阵快速乘方
fastSum(n) = 2 * fastSum(n/2) + n^2/4 (n是偶数)
fastSum(n) = fastSum(n-1) + n (n为奇数)
int fastSum(int n) {
if (n==1) return 1;
if (n%2==1) return fastSum(n-1) + n;
return 2*fastSum(n/2) + (n/2)*(n/2);
}
矩阵乘方,m非常大时,会耗费比较长的时间。O(n^3m)
,就可以减半
class SquareMatrix;
SquareMatrix identity(int n);
SquareMatrix pow(const SquareMatrix& A, int m) {
if (m==0) return identity(A.size());
if (m%2 > 0) return pow(A, m-1) * A;
SquareMatrix half = pow(A, m/2);
return half * half;
}
如果m是奇数,
示例:归并排序和快速排序
归并排序把数列从中间分开O(nlgn),而快排分成左小右大O(nlgn)。
示例:Karatsuba快速乘积算法
大整数相乘
练习题1:四叉树问题
把大量坐标数据压缩保存到内存空间。如用字符串的形式对黑白图像进行如下压缩。
- 图像所有像素为黑色,结果为b。
- 图像所有像素为白色,结果为w。
- 不同颜色,一分为二,递归对四个小图像进行压缩,如xwwwb。
练习题2:切割篱笆
用长篱笆补短篱笆,保证切割出的面积最大的长方形。
第l个木板到第r个的长方形面积:(r-l+1) x min(h)
三种可能,求最大
- 最大全部在左侧的子问题中
- 最大全部在右侧的子问题中
- 最大横跨左右两侧的子问题中
练习题3:粉丝见面会
a~d表示明星,0~5为粉丝,按顺序拥抱,不过男明星和男粉丝不拥抱,而是握手。计算所有成员同时和粉丝拥抱的情况。
O((N-M)M)时间复杂度,但是N和M可能是近20万,不能在限定时间内完成计算。
把它看成大数相乘就能快速解决
调换A的顺序,得到
int hugs(const string& members, const string& fans) {
int N = member.size(), M=fans.size();
vector<int> A(N), B(M);
for (int i=0; i<N; i++) A[i] = (members[i]=='M');
for (int i=0; i<M; i++) B[M-i-1] = (fans[i] == 'M');
vector<int> C = karasuba(A, B);
int allHugs = 0;
for (int i=N-1; i<M; i++)
if (C[i] == 0)
++allHugs;
return allHugs;
}
时间复杂度O(n^(lg3))