分支步骤
步骤:
1)分解(Divide):将原问题分为若干个规模较小、相互独立,形式与原 问题一样的子问题;
2)解决(Conquer):若子问题规模较小、可直接求解时则直接解(称基本情况(base case));否则 “递归”地求解各个子问题,即继续将较大子问题分 解为更小的子问题,然后重复上述计算过程。
3)合并(Combine):将子问题的解合并成原问题的解。
分治实例
归并排序
- 应用步骤:
1)分解(Divide):将原序列分为两个规模为原来一半的子序列
2)解决(Conquer):当子序列只有1个元素时,满足有序;否则 “递归” 地求解各个子问题。
3)合并(Combine):将两个有序的子数组合并成有序的原数组。
- 过程描述:
MERGE-SORT(A,p,r)
MERGE(A,p,q,r)
- 时间复杂度分析:
(求解递归式可得:)
最大子数组
已知数组A,在A中寻找和最大的非空连续子数组。
- 应用步骤:
1)分解(Divide):将原数组分为两个规模一样的左右两个子数组。
2)解决(Conquer):分别求解两个子数组的最大的非空连续子数组。
3)合并(Combine):最终答案要么整体都在左边或右边子数组中,要么横跨左右,这种情况在合并时考虑,时间复杂度。
- 时间复杂度:
有
Strassen矩阵乘法
减少乘法次数,增加加法次数。
先考虑两2 × 2 的矩阵相乘:
令:
P=(a11+a22 )(b11+b22 )
Q=(a21+a22 ) b11
R=a11 (b12-b22 )
S=a22 (b21-b11 )
T=(a11 + a12 )b22
U=(a11 – a21 ) (b11 + b12 )
V=(a12 – a22 ) (b21 + b22)
则有:
c11 = P+S-T+V = (a11+a22 )(b11+b22 ) + a22 (b21-b11 ) -(a11 + a12 )b22+(a12 – a22 ) (b21 + b22 ) ≡ a11b11+a12b21
c12 = R+T ≡ a11b12+a12b22
c21 = Q+S ≡ a21b11+a22b21
c22 = P+R-Q-U ≡ a21b12+a22b
现在对于n阶矩阵,采用分块:(设,若不然可通过在A和B中补0使之变成阶是2的幂的方阵),然后采用Strassen乘法。
- 时间复杂度:
有:
(但实际上,采用Strassen算法作递归运算,需要创建大量的动态二维数组,其中分配堆 内存空间将占用大量计算时间,从而掩盖了Strassen算法的优势。于是对Strassen算法做出改进,设定一个界限。当 n< 界限时,使用普通法计算矩阵,而不继续分治递归。需要合理设置界限,不同环境(硬件配置)下界限不同。矩阵乘法一般意义上还是选择的是朴素的方法,只有当矩阵变稠密,而且矩阵的阶数很大时,才会考虑使用Strassen算法。)
最近点对问题
可以参考这一题
求解递归式
用T(n)表示对规模为n的问题进行求解的时间,则规模分别为n1和n2的子问题的求解时间可表示为T(n1 )和T(n2)。
一般地,
若有,则
如何化简递归式,得到形式简单的限界函数?
代换法
递归树法
主方法
预处理:
1):n为正整数(因为n通常表示数据的个数)
2):忽略递归式的边界条件。(递归式的解随T(1)值变化的改变不会超过常数因子)
3):对上取整、下取整运算做合理简化(忽略)。
代数法
先猜测解的形式,然后用数学归纳法求出解中的常数,并证明解是正确的。
必要的时候要做一些技术处理
1)去掉一个低阶项
2)变量代换:
如:
令 ,有
再令,得
即化为了熟悉的形式,求解出S(m)再回带。
递归树
以例子说明:
如
递归树演化如下:
=> => =>
所有节点相加:
化简得:
再用代换法证明其正确性:
要使得成立,只要即可,所以猜测成立。
主方法
针对具有如形式的递推式,其中a, b为常数,,,没有考虑n/b的取整问题,即省略了下取整、上取整,因为其本质上不影响对递归式渐近行为的分析。
- 若对于某常数,有,则
- 若,则
- 若对于某常数,有,且对于某常数,所有足够大的n,有,则