...
本文正在参加 人工智能创作者扶持计划
本系列分治法重点内容一览:
- 详解当求解子问题与划分+合并子问题时间复杂度相同时,分治法的整体复杂度
- 详解最短点对问题为什么可以做到线性的时间复杂度,包括鸽舍原理等。
本系列主要为mooc学习笔记,重点内容为课程未讲解清楚且由笔者独立思考补充之处,如有错误恳请不吝指出。
为什么要学习分治法
使用到分治法求解的问题包括
- 二分查找
- 大整数乘法
- 棋盘覆盖
- 归并排序
- 快速排序
- 线性时间选择
- 最短点对问题
- 循环赛日程表
- 汉诺塔
分治法的设计思想
将一个难以直接求解的大问题,
分解成若干个规模较小的子问题,
递归地求解这些子问题,
然后合并子问题的解得到原问题的解。
注意:
子问题与原问题形式相同
子问题可以彼此独立的求解,
即子问题之间不包含公共的子问题
子问题的规模缩小到一定程度就可以容易地直接求解
分治法的求解过程
划分子问题:
将原问题分解为若干个规模较小,相互独立, 与原问题形式相同的子问题。
求解子问题:
若子问题规模较小而容易被解决则直接求解,
否则递归地求解各个子问题。
合并子问题:
将各个子问题的解合并为原问题的解。
分治法的一般算法描述
Divide_Conquer ( P ) {
if ( P的规模足够小 )
直接求解P;
else
分解为k个子问题P1, P2, …, Pk;
for (i=1; i<=k; i++) {
yi = Divide_Conquer(Pi);
}
return Combine(y1, …, yk);
}
分治法的分析方法
按照递归算法的分析过程,首先建立递归方程,然后求解递归方程
建立递归方程
划分阶段的时间复杂性
求解子问题阶段的时间复杂性
- 递归调用时间=aT(n/b)
合并子问题的解阶段的时间复杂性
- 假设合并时间=C(n)
递归方程时间复杂度
T(n)T(n)=Θ(1)=aT(n/b)+D(n)+C(n)n<c 否则
这里的c是常数的意思,一般认为如果小于4时,直接求解更快。
然后求解递归方程
T(n)={Θ(1)aT(n/b)+f(n)n<c 否则
大小为n 的原问题分成若干个大小为n/b的子问题,
其中a个子问题需要求解,
而f(n)是划分加合并各个子问题的解需要的工作量。即f(n)=D(n)+C(n)
分治法总体时间复杂度(‼️‼️)
T(n)=⎩⎨⎧O(nlogba)O(nlogbalog2n)O(f(n))Θ(nlogba)>f(n)Θ(nlogba)=f(n)Θ(nlogba)<f(n)
通过这个时间复杂度公式来看,
当分解的问题数b≥需要求解的子问题a时,
会取得比O(n)更好的时间复杂度
当f(n)=O(nlogba)时,
递归深度是 logbn,划分及合并的额外工作的总时间复杂度取决于递归深度,故为 O(logbn)。
如图所示,分治法中的b必然大于等于2且为整数,n必然大于1,故,当n>1,b>2时,logbn<log2n
所以以2为底是时间复杂度的上界,因此我们可以将 logbn 写成 log2n。
典型分治法:二分法的时间复杂度
二分法的递归方程
T(n)=2T(n/2)+f(n)
故二分法的时间复杂度如下
T(n)=⎩⎨⎧O(n)O(nlog2n)O(f(n))Θ(n)>f(n)Θ(n)=f(n)Θ(n)<f(n)
分治算法的有效性很大程度上依赖于合并步骤的实现效率。
典型分治法实例:归并排序
基本思想:将待排序元素分成大小大致相同的2个子序列,分别对2个子序列进行归并排序,最终将排好序的子序列合并成为所要求的排好序的序列。
其实就是二分法T(n)={