递归 —— 时间复杂度 --lecoode记录

给出一个递归算法,其时间复杂度 O(T) 通常是递归调用的数量(记作 R) 和计算的时间复杂度的乘积(表示为 O(s))的乘积:

O(T)=R∗O(s)

示例

也许你还记得,在反转字符串问题中,我们需要以相反的顺序打印字符串,解决问题的递归关系可以表示如下:

printReverse(str) = printReverse(str[1…n]) + print(str[0])

其中 str[1…n] 是输入字符串 str 的子串,仅不含前导字符 str[0]。

如您所见,该函数将被递归调用 n 次,其中 n 是输入字符串的大小。在每次递归结束时,我们只是打印前导字符,因此该特定操作的时间复杂度是恒定的,即 O(1)。

总而言之,我们的递归函数 printReverse(str) 的总体时间复杂度为 O(printReverse)=n∗O(1)=O(n)。

执行树

对于递归函数,递归调用的数量很少与输入的大小呈线性关系。如果你还记得我们在前一章中讨论过的斐波那契数问题,其递推关系被定义为 f(n) = f(n-1) + f(n-2)。乍一看,在执行斐波那契函数期间计算递归调用的数量似乎并不简单。

在这种情况下,最好采用执行树,这是一个用于表示递归函数的执行流程的树。树中的每个节点都表示递归函数的调用。因此,树中的节点总数对应于执行期间的递归调用的数量。

递归函数的执行树将形成 n 叉树,其中 n 作为递推关系中出现递归的次数。例如,斐波那契函数的执行将形成二叉树,下面的图示展现了用于计算斐波纳契数 f(4) 的执行树。
在这里插入图片描述
在 n 层的完全二叉树中,节点的总数为 2^n -1。
因此 f(n) 中递归数目的上限(尽管不严格)也是2^n-1 。
那么我们可以估计 f(n) 的时间复杂度为 O(2^n)

记忆化(Memoization)

记忆化技术可以大大减少递归调用的数量,即减少执行树中的分支数量。在使用记忆化分析递归算法的时间复杂度时,也应该考虑到这种减少。

让我们回到斐波纳契数的例子。通过记忆化技术,我们保存每个索引 n 对应的的斐波那契数的结果。我们确信每个斐波那契数的计算只会发生一次。而从递推关系来看,斐波纳契数 f(n) 将取决于其所有 n-1 个先验斐波纳契数。结果,计算 f(n) 的递归将被调用 n-1 次以计算它所依赖的所有先验数字。

现在,我们可以简单地应用我们在本章开头介绍的公式来计算时间复杂度,即 O(1)∗n=O(n)。记忆化技术不仅可以优化算法的时间复杂度,还可以简化时间复杂度的计算。

在下一篇文章中,我们将讨论如何估计递归算法的空间复杂度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值