一、特点与框架
1、特点
-
将原问题归约为规模小的子问题,子问题与原问题具有相同的性质。
-
算法可以递归也可以迭代实现。
2、框架
Divide-and-Conquer (P)
-
if |P|\le c then S(P)
-
divide P into P_1, P_2, …, P_k
-
for i\leftarrow 1 to k
-
y_i\leftarrow Divide-and-Conquer (P_i)
-
-
Return Merge (y_1, y_2, … , y_k)
二、递推方程
-
f(n)=\Sigma_{i=1}^kf(n-i)+g(n)
-
Hanoi
-
-
f(n)=af(\frac{n}{b})+d(n)
-
二分检索、归并排序
-
d(n)=c时,T(n)=\begin{cases}O(n^{log_ba}),\quad a\ne1\\O(logn), \quad a=1\end{cases}
-
d(n)= cn时,T(n)=\begin{cases}O(n), \quad a<b\\O(n^{log_ba}),\quad a>b\\O(nlogn),\quad a=b\end{cases}
-
三、应用
-
芯片测试
-
快速排序
-
幂乘算法
四、改进策略
1、减少子问题数
(1)适用场景
-
适用于子问题个数多,划分和综合工作量不太大,时间复杂度函数T(n) = n^{log_ba}的情况。
-
利用子问题依赖关系,用某些子问题解的代数表达式表示另一些子问题的解,减少独立计算子问题个数。综合解的工作量可能会增加,但增加的工作量不影响 T(n)的阶。
(2)举例
-
矩阵乘法
-
分块相乘:f(n)=8f(\frac{n}{2})+cn^2. T(n)=O(n^3).
-
Strassen矩阵乘法:f(n)=7f(\frac{n}{2})+\frac{9}{2}n^2. T(n)=O(n^{log7}).
-
-
整数位乘问题:输入:X, Y是n位二进制数,n=2^k。输出:XY。
-
简单划分:X= A2^{n/2} +B, Y= C2^{n/2} +D
XY= AC2^n + (AD + BC) 2^{n/2} + BD f(n) = 4f(n/2)+O(n),T(n)=O(n^2)
-
利用子问题之间的依赖关系:AD+BC = (A-B)(D-C) + AC + BD f(n) = 3 f(n/2) + cn,T(n)=O(n^{log3})
-
利用快速傅里叶变换可以得到时间复杂度为O(nlogn)的算法
-
2、增加预处理
(1)适用场景
-
递推方程为f(n)=af(\frac{n}{b})+d(n)时,通过增加预处理来减少d(n)
(2)举例
-
平面点对问题
-
原算法:在每次划分时对子问题数组重新排序
-
改进算法:在递归前对 X,Y 排序,作为预处理。划分时对排序的数组 X,Y 进行拆分,得到针对子问题 P_L的数组 X_L,Y_L及针对子问题 P_R的数组 X_R,Y_R
-
四、典型算法讲解
1、选择问题
(1)选第2大
①思路分析
-
成为第二大数的条件:仅在与最大数的比较中被淘汰
-
要确定第二大数,必须知道最大数
-
在确定最大数的过程中记录下被最大数直接淘汰的元素.
-
在上述范围(被最大数直接淘汰的数)内的最大数就是第二大数.
-
设计思想: 用空间换时间
②锦标赛算法
-
两两分组比较,大者进入下一轮,直到剩下 1个元素 max 为止
-
在每次比较中淘汰较小元素,将被淘汰元素记录在淘汰它的元素 的链表上
-
检查 max 的链表,从中找到最大元,即second
③伪码
输入: n个数的数组 L,输出: second
-
k←n // 参与淘汰的元素数
-
将k个元素两两1组, 分成 k/2 组
-
每组的2个数比较,找到较大数
-
将被淘汰数记入较大数的链表
-
if k 为奇数 ,then k←k/2 +1
-
else k左k/2
-
-
if k>1 then goto 2
-
max←最大数
-
second←max 的链表中的最大
④时间复杂度
-
找max:n-1次比较,因为淘汰了n-1个元素
-
找second:O(logn)
(2)一般选择问题:选第k小
①简单算法
-
调用 k 次选最小算法,时间复杂度为O(kn)
-
先排序,然后输出第 k 小的数时间复杂度为O(nlogn)
②分治算法设计思路
-
用某个元素 m作为标准将 S 划分成 S1 与 S2,其中 S1的元素小于 m,S2 的元素大于等于 m。
-
如果 k≤|S1|,则在 S1中找第 k 小。如果 k=|S1|+1,则m是第 k 小 如果 k > |S1|+1,则在 S2中找第k-|S1|-1小。
③伪码
④时间复杂度
|M|与归约后子问题规模之和小于 n,递归树每行的工作量构成公比小于 1的等比级数,算法复杂度才是O(n)
⑤补充
用stl库中的nth_element函数可以实现这一功能,利用了随机算法,原理类似快速排序,期望复杂度是O(n)。见C++ STL nth_element原理与应用_nth element 原理-CSDN博客
2、信号平滑处理
(1)快速傅里叶变换(FFT)
(2)卷积
3、计算几何
(1)计算平面点集的凸包
参考:北京大学《算法设计与分析》MOOC,授课教师汪小林