算法导论------递归算法的时间复杂度求解

目录


1.算法设计与分析概述

  在总结递归算法的时间复杂度分析之前,应该明确几组概念。
  算法仅仅是求解问题的解决方案,这个解决方案本身并不是问题的答案,而是能获得答案的指令序列。只有通过执行算法才可以获得求解问题的答案。
  从算法是否递归调用的角度看,算法可以分为非递归算法和递归算法。
  非递归算法时间复杂度分析较为简单,通常是计算算法中基本语句执行次数,一般都是一个关于问题规模n的表达式,然后用渐近符号 ΘOoΩω 表示出算法的时间复杂度。
  递归算法是采用分治的方法,把一个“大问题”分解出若干个相似的“小问题”求解。在分析算法复杂度时,关键是根据递归过程建立递推关系式,然后求解递推关系式,得到算法执行的时间表达式(一般都与问题规模n相关),最后用渐近符号 ΘOoΩω 表示出算法的时间复杂度。
  在《算法导论》、《算法设计与分析》这2门课中,我们已经学习一些通用的算法设计技术,如增量法、分治法、贪心法、动态规划、线性规划、回溯法、分支限界法等;在算法设计完成后,对算法的复杂度进行分析是必然的,所以本篇的中心将围绕算法时间复杂度展开。


2.非递归算法分析

例1:如果算法的执行时间不随着问题规模n的增加而增长,它的基本语句执行的次数是固定的,总的时间由一个常数来限界。此类算法的时间复杂度是O(1)。
例2:当有若干个循环语句时,时间复杂度是由嵌套层数最多的循环语句中的基本语句的执行次数决定。如下

void fun(int n){
    int x=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
            for(int k=1;k<=j;k++){
                x++;             //基本语句
            }
        }
    }
}

解:该算法的基本语句是x++;所以
f(n)=i=1nj=1ik=1j1=i=1nj=1ij=i=1ni(i+1)2=....=O(n3) ,时间复杂度为 O(n3)

虽然非递归算法的时间复杂度比较好分析,但往往需要用到多项式的求和技巧和放缩技巧,如:

  1. 等差数列 {ak} 求和: k=1nak=n(a1+an)2
  2. 等比数列 {aqk} 求和: k=0naqk=a(1qn+1)1q
  3. 调和级数 {1k} 求和: k=1n1k=lnn+O(1) (需要用微积分知识证明)
  4. 对数级数 lg1+lg2+...+lgn=lg(n!)=Θ(nlgn) (利用Stirling公式证明)
  5. 放缩1:用序列中的最大项代替序列中的每个项,这种方法可以表示为: k=1naknamax
  6. 放缩2:在等比数列中,假设存在常数 r<1 ,使得 ak+1akr 对一切 k0 成立,那么有:
    k=0nakk=0a0rk=a0k=0rk=a01r
  7. 放缩3:对每个b>1和每个a>0,都有 logbn=o(na)
  8. 1+3+5+...+(2n1)=n2
  9. 12+22+32+...+n2=16n(n+1)(2n+1)=O(n3) (幂方级数,上限比幂次高出一阶)
  10. 13+23+33+...n3=[12n(n+1)]2=O(n4)
  11. 各种收敛级数,不再一 一列举。

小结:以上这些求和及放缩技巧在下面的递归树求总代价、主定理证明过程中也要用到,非常的重要!


3.递归算法分析

3.1利用数列知识

  1. 累加法:递推关系式为 an+1an=f(n) 采用累加法。
  2. 累乘法:递推关系式为 an+1an=f(n) 采用累乘法。
  3. 构造法:递推关系式为(1) aa+1=pan+q ,(2) aa+1=pan+qn ,都可以通过恒等变形,构造出等差或等比数列,利用等差或等比数列的定义进行解题,其中的构造方法可通过待定系数法来进行。
  4. 和化项法:递推公式为 Sn=f(n) Sn=f(an) 一般利用
    an={S1SnSn1n=1n>=2
  5. 用特征方程求解递推方程(感觉比较生僻,不做解释)
  6. 迭代法: 从原始递推方程开始,反复将对于递推方程左边的函数用右边的等式代入,直到得到初值,然后将所得的结果进行化简。
    例如在调用归并排序mergeSort(a,0,n-1)对数组 a[0...n1] 排序时,执行时间 T(n) 的递推关系式为:
    T(n)={O(1)2T(n2)+O(n)n=1n>=2

    其中, O(n) 为merge()所需要的时间,设为 cn (c为正常量)。因此:
    T(n)=2T(n2)+cn=2(2T(n4)+cn2)+cn=22T(n4)+2cn=23T(n8)+3cn=...=2kT(n2k)+kcn=nO(1)+cnlog2n=O(nlog2n),(n=2k,k=log2n)

  忽略求解细节。在我们求解递归式时,因为最终是要求得一个时间上限,所以在求解时常常省略一些细节。比如mergeSort(a,0,n-1)运行时间的实际递归式应该是:

T(n)=O(1)T(n2)+T(n2)+O(n)n=1n>=2

但我们忽略这些上取整、下取整以及边界条件,甚至假设问题规模 n=2k ,这都是为方便求解而忽略的细节。经验和一些定理告诉我们,这些细节不会影响算法时间复杂度的渐近界。

  类似的,我们也可以用迭代法求解汉诺塔递归求解时的时间复杂度。但遗憾的是,迭代法一般适用于一阶的递推方程。对于二阶及以上(即 T(n) )的递推方程,迭代法将导致迭代后的项太多,从而使得求和公式过于复杂,因此需要将递推方程化简,利用差消法等技巧将高阶递推方程化为一阶递推方程。如在求快速排序算法平均时间复杂度 T(n) 的递推方程, T(n) 依赖 T(n1)T(n2)...T(1) 等所有的项,这样的递推方程也称为全部历史递推方程。(这里省略快速排序算法平均复杂度T(n)的求解过程)

小结:上面6种递推关系是高中、本科知识,在此重点介绍了迭代法,其它几种方法虽未在本篇中使用,但可以加深对递推式求解的认识。


3.2代入法

代入法实质上就是数学归纳法,因此求递推式分为两步:

  1. 猜测解的形式;
  2. 用数学归纳法求出解中的常数,并证明解是正确的。

  遗憾的是并不存在通用的方法来猜测递归式的正确解,需要凭借经验,偶尔还需要创造力。即使猜出了递归式解的渐近界,也有可能在数学归纳证明时莫名其妙的失败。正是由于该方法技术细节较为难掌握,因此这个方法不适合用来求解递归方程,反而比较适合作为其他方法检验手段。在此不做总结。可以翻阅《算法导论》进行学习。


3.3递归树

  递归树是一棵结点带权值的树。初始的递归树只有一个结点,它的权标记为 T(n) ;然后按照递归树的迭代规则不断进行迭代,每迭代一次递归树就增加一层,直到树中不再含有权值为函数的结点(即叶结点都为 T(1) )。下面以递归方程

T(n)={O(1)2T(n2)+O(n)n=1n>=2;(n=2k,k=log2n)
来讲述递归树的迭代规则。

  • 第一步: 把根结点 T(n) 用根是 cn 、左结点为 T(n2) 、右结点为 T(n2) 的子树代替(即:以分解、合并子问题需要的代价为根,分解得到的子问题为叶的子树。其中常量c代表求解规模为1的问题所需的时间);(如下如 (a)(b)
  • 第二步:把叶结点按照“第一步”的方式展开; T(n2) 用根是 cn/2 、左节点为 T(n4) 、右结点为 T(n4) 的子树代替。(如下如 (b)(c)
  • 第三步:反复按照“第一步”的方式迭代,每迭代一次递归树就增加一层,直到树中不再含有权值为函数的结点(即叶结点都为 T(1) )。(如下如 (c)(d)

这里写图片描述

  在得到递归树后,将树中每层中的代价求和,得到每层代价,然后将所有层的代价求和,得到所有层次的递归调用的总代价。在上图(d)部分中,完全展开的递归树高度为 lgn (树高为根结点到叶结点最长简单路径上边的数目),所有递归树具有 lgn+1 层,所以总代价为 cn(lgn+1) ,所有时间复杂度为 Θ(nlgn)

  总结:递归树模型求解递归方程,本质上就是迭代思想的应用,利用递归方程迭代展开过程构造对应的递归树,然后把每层的时间代价进行求和。不过递归树模型更直观,同时递归树也克服了二阶及更高阶递推方程不方便迭代展开的痛点。


3.4主方法求解递推式

  主方法为如下形式的递归式提供了一种“菜谱”式的求解方法,如下所示

T(n)=aT(n/b)+f(n)

其中 a1 b>1 是常数, f(n) 是渐近正函数。这个递推式将规模为n的问题分解为a个子问题,每个子问题的规模为 n/b ,a个子问题递归地求解,每个花费时间 T(n/b) 。函数 f(n) 包含了问题分解和子问题解合并的代价。同样,这个递归式也没有考虑上取整、下取整、边界条件等,结果不会影响递归式的渐近性质。

定理4.1(主定理) 令a≥1和b>1是常数, f(n) 是一个函数, T(n) 是定义在非负整数上的递归式:

T(n)=aT(n/b)+f(n)

其中我们将 n/b 解释为 n/b n/b 。那么 T(n) 有如下渐近界:
1. 若对某个常数 ε>0 f(n)=O(n(logba)ε) ,则 T(n)=Θ(nlogba)
2. 若 f(n)=Θ(nlogba) ,则 T(n)=Θ(nlogbalgn)
3. 若对某个常数 ε>0 f(n)=Ω(n(logba)+ε) ,且对某个常数 c<1 和所有足够大的n有 af(n/b)cf(n) ,则 T(n)=Θ(f(n))

  在使用主定理之前,要比较 f(n)(nlogba) 的大小,这个大小不是算术意义上的大小比较,而是要在多项式意义上比较。以上三种情况在多项式意义上并未覆盖 f(n) 的所有可能性。情况1和情况2之间有一定间隙;情况2和情况3之间也有一定间隙。如果f(n)落在这两个间隙中,或者情况3中 正则条件不成立,就不能使用主方法来求递归式。
  如在递归式: T(n)=2T(n/2)+nlgn 中,因为 nlogba=n<f(n)=nlgn ,但是 f(n) 并不大于n一个多项式因子 nε ,因为对于给定的 ε>0 当n足够大时,均有 nε>lgn 。所以找不到这样 ε>0 ,该递归式落入了情况2和情况3之间的间隙,不能使用主定理。
  最后给出主定理应用的几个练习题:

主方法联系


4.参考资料

  1. 《算法导论》第三版
  2. 《算法设计与分析》屈婉玲著,清华大学出版社
  3. 《算法设计与分析》李春葆著,清华大学出版社
  • 86
    点赞
  • 364
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
### 回答1: 算法10-12~10-14是关于归并排序的。 归并排序是一种基于分治思想的排序算法,它将待排序的序列分成两个子序列,对每个子序列进行排序,然后将两个已排序的子序列合并成一个有序序列。 算法10-12是归并排序的递归实现,它将待排序的序列不断地分成两个子序列,直到每个子序列只有一个元素,然后将这些子序列两两合并,直到最终得到一个有序序列。 算法10-13是归并排序的非递归实现,它使用迭代的方将待排序的序列分成若干个子序列,然后将相邻的子序列两两合并,直到最终得到一个有序序列。 算法10-14是归并排序的优化实现,它使用插入排序来处理长度较小的子序列,以提高排序效率。具体来说,当子序列的长度小于等于一定阈值时,使用插入排序来排序,否则使用归并排序来排序。 ### 回答2: 归并排序是一种基于分治思想的排序算法。该算法的核心思想是将待排序的序列不断分割成更小的子序列,直到每个子序列只有一个元素,然后将这些子序列逐一合并,直到整个序列有序。因此,归并排序分为两个主要过程,分别是分割过程和合并过程。 分割过程:归并排序首先将待排序的序列均分成两个子序列,然后递归地对子序列进行分割,直到每个子序列只有一个元素。 合并过程:将两个有序的子序列合并成一个有序的序列。此时需要定义两个指针 i 和 j 分别指向两个子序列的起始位置,比较两个指针所指向的元素的大小,将较小的元素放到结果数组中并将指针向后移动,直到有一个子序列的指针到达了序列的末尾,然后将另一个子序列中未处理的元素依次放入结果数组中。 归并排序的时间复杂度为 O(N*logN),其中 N 为序列的长度。因此,归并排序是一种性能较好且稳定的排序算法,但由于其需要使用临时数组来辅助排序,因此空间复杂度为 O(N)。 总之,归并排序是一种高效且稳定的排序算法,适用于各种规模的序列。在实际应用中,可以使用多线程或并发编程来加速归并排序的执行过程,提高排序效率。 ### 回答3: 归并排序是一种常见的排序算法,采用了分治的思想,可以在最坏情况下也达到O(nlogn)的时间复杂度。它将一个大问题拆分成小问题,然后逐个解决小问题。下面是归并排序的基本流程: 1. 将待排序的序列按照中间位置分为两个子序列,分别排序。 2. 合并两个有序的子序列,形成一个新的有序序列。 具体实现时,我们可以使用递归或迭代两种方。下面以递归来说明归并排序的实现。 算法10-12:递归实现归并排序 1. 将序列按中间位置分为左右两个子序列。 2. 对左右子序列分别递归调用归并排序。 3. 合并左右子序列。 算法10-13:合并两个有序序列 1. 定义一个新序列,长度为左右子序列之和。 2. 从两个子序列的头开始比较,将小的元素放入新序列中。 3. 将剩余的元素全部复制到新序列中。 算法10-14:归并排序时间复杂度 1. 分解阶段:将序列分为两个子序列,时间复杂度为O(logn)。 2. 合并阶段:合并两个有序序列,时间复杂度为O(n)。 3. 总时间复杂度为O(nlogn)。 归并排序虽然时间复杂度较低,但空间复杂度为O(n),需要额外的存储空间来存储临时序列。但相比其他排序算法,归并排序具有稳定性,适合处理大规模数据的排序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GNG

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值