递归 - 时间复杂度

在本文中, 我们主要介绍如何分析递归算法程序中的时间复杂度。.
在一个递归程序中, 它的时间复杂度 O(T) 一般来说就是他总共递归调用的次数 (定义为 R) 以及每次调用时所花费的耗时 (定义为 O(s)) ,这样我们就可以得出:
(T) = R * O(T) = R∗O(s)
下面让我们来看几个栗子:
 

线性的栗子


正如之前的问题 printReverse所描述的, 需要把一个字串逆序输出. 其中一种递归的解法如下所示:
printReverse(str) = printReverse(str[1...n]) + print(str[0])
其中 str[1...n] 是输入的字串 str 去除了首字母str[0]的切分子串, .
显而易见,这个算法会连续调用 n 次, 这个 n 也就该输入字串的长度. 在每次递归的最后, 我们只打印首字母, 因此该算法每次调用递归所耗费的时间为常量, 即为 {\mathcal{O}(1)}O(1).
把次数和每次耗时进行合计,该递归程序 printReverse(str) 的耗时即为 (printReverse) = n * O(1) = O(printReverse)=n∗O(1)=O(n).
 

执行树


对于递归函数, 像上面的线性化递归调用的栗子其实是很少的,更多的是非线性的. 例如, 之前章节我们讨论的 Fibonacci number ,它的递归关系就定义为更复杂的 f(n) = f(n-1) + f(n-2). 乍看之下, 很难一下子去计算出斐波那契函数的递归调用次数 -_-.
在这个例子里, 我们最好使用 execution tree 这个工具可以用来直观地表示递归程序的执行流. 树中的每一个节点都表示每次对应的递归程序调用. 因此,树中的节点总数与整个递归过程调用的总数相对应。
递归函数的执行树会形成一个 n-ary tree, 其中n 就是这个程序执行下来递归调用的次数. 例如, 斐波那契函数的执行流就是一个二叉树, 如下图所示就是计算 f(4) 的流程树:
 
在一个 n 层的满二叉树, 所有节点数总和应该是 
2^n - 1
 
 
. 因此, 对于递归程序 f(n) 总调用次数的上限也应该是 
{2^n -1}
 
. 所以, 我们得出了递归程序 f(n) 的时间复杂度即为 
{\mathcal{O}(2^n)}
 
 

记忆化


在前面的章节中, 我们讨论过用来优化递归算法时间复杂度的记忆化方法. 通过存储和重复使用中间变量, 记忆化能够极大地降低递归程序的调用次数, 换个说法就是减少执行树中的递归调用分支. 在分析试用了记忆化的递归调用程序的时间复杂度时,千万要记得考虑这种(分支减少的)情况。.
让我们重新再回看前面斐波那契额数列的栗子. 使用记忆化方法的话,我们每次都将斐波那契额数列在 n.节点下的存储, 于是我们可以确保对于每个节点计算需要的递归调用只需要一次. 而且我们知道斐波那契额数列的递归关系是每个 f(n) 都依赖前一个 n-1 的结果. 最终使得计算 f(n) 只调用 n-1 次 之前已经计算好的结果即可. 
现在, 我们可以很轻易的通过前面介绍的公式 O(1)∗n=O(n) .来计算斐波那契额数列函数的时间复杂度。记忆化不仅仅优化算法的时间复杂度,同样也简化了对于时间复杂度的计算。
在下一篇文章中, 我们将讨论如何估算递归程序的空间复杂度.
 
 
 
 原文地址:https://leetcode.com/explore/learn/card/recursion-i/256/complexity-analysis/1669/
 
 
 

LeetCode 递归(Recursion) 培训专题 讲解文章翻译 (附链接)的更多相关文章

  1. LeetCode递归 -2(Recursion) 培训专题 讲解文章翻译 (附链接) (2019-04-09 15:50)

    递归 - 空间复杂度  在本文中, 我们将讨论如何分析递归算法的空间复杂度. 在计算递归算法的空间复杂度时,最需要考虑的两个部分就是: 递归相关空间 (recursion related space) ...

  2. Activity Process Task Application 专题讲解

    Activity Process Task Application 专题讲解 Activity.和进程 为了阅读方便,将文档转成pdf http://files.cnblogs.com/franksu ...

  3. Leetcode之回溯法专题-216. 组合总和 III(Combination Sum III)

    Leetcode之回溯法专题-216. 组合总和 III(Combination Sum III) 同类题目: Leetcode之回溯法专题-39. 组合总数(Combination Sum) Lee ...

  4. 算法与数据结构基础 - 递归(Recursion)

    递归基础 递归(Recursion)是常见常用的算法,是DFS.分治法.回溯.二叉树遍历等方法的基础,典型的应用递归的问题有求阶乘.汉诺塔.斐波那契数列等,可视化过程. 应用递归算法一般分三步,一是定 ...

  5. Atitit  循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate).

    Atitit  循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate). 1.1. 循环算是最基础的概念, 凡是重复执行一段代码, 都可以称之为循环. ...

  6. 循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate)的区别

    表示“重复”这个含义的词有很多, 比如循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate). 循环算是最基础的概念, 凡是重复执行一段代码, 都可以称 ...

  7. 003_循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate)的区别

    表示“重复”这个含义的词有很多, 比如循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate). 循环算是最基础的概念, 凡是重复执行一段代码, 都可以称 ...

  8. Leetcode之分治法专题-169. 求众数(Majority Element)

    Leetcode之分治法专题-169. 求众数(Majority Element) 给定一个大小为 n 的数组,找到其中的众数.众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是 ...

  9. Leetcode之回溯法专题-212. 单词搜索 II(Word Search II)

    Leetcode之回溯法专题-212. 单词搜索 II(Word Search II) 给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词. 单 ...

随机推荐

  1. 使用原生JS进行字符串转对象

    字符串转对象 目的 工作中如果需要原生 JS 完成字符转对象的话可以通过 JSON.parse(str), 但是这个方法是ES5中才出现, 如果需要兼容低版本就需要其它方法 使用原生 JS 解决字符串 ...

  2. 根文件系统及Busybox简介

    转:http://blog.csdn.net/wqc02/article/details/8930184 1.根文件系统简介...2 2.Busybox简介...2 2.1Busybox简介...2 ...

  3. oracle表空间操作语句

    1.查看所有表空间及表空间大小: select tablespace_name ,sum(bytes) / 1024 / 1024 as MB from dba_data_files group by ...

  4. 【mybatis】mybatis中update更新原来的值加1

    示例代码: floor的值 = floor原来的值+要更新的差距值 <update id="updateFloor" parameterType="com.pise ...

  5. springBoot框架的一些概念

    Spring的发展史1. Spring1.x 时代 在Spring1.x时代,都是通过xml文件配置bean,随着项目的不断扩大,需要将xml配置分放到不同的配置文件中,需要频繁的在java类和xml ...

  6. fastjson用法&Gson

    <dependency>    <groupId>com.google.code.gson</groupId>   <artifactId>gson&l ...

  7. Android——DEBUG 堆栈

    当android系统执行出现死机等致命错误的时候.通常会有堆栈的DEBUG信息打印,一般直接看根本看不出问题是出在哪里!记录下我android4.2 的DEBUG 堆栈log的方法. 撰写不易,转载请 ...

  8. 用C++实现文件压缩(1.5)

    今天主要做的就是,将完成huffman编码的数据以二进制的形式写入文件中.这是个挺苦逼的活. 不过好在我以前玩过一段时间的单片机,所有能够较好的实现位运算,一位一位的将数据存放到缓冲区中,然后统一写入 ...

  9. 小伙子又乱码了吧-Java字符编码原理总结

    前提 配合前面阅读的I/O和NIO的资料,现在总结一下关于字符集和乱码问题的原理和解决方案.参考资料: 码表ASCII Unicode GBK UTF-8 字符编码笔记ASCII,Unicode和UT ...

  10. 【Java】Java_08 字符型与布尔值

    1.字符型(2个字节) 单引号用来表示字符常量.例如‘A’是一个字符,它与“A”是不同的,“A”表示含有一个字符的字符串 char 类型用来表示在Unicode编码表中的字符 Unicode编码被设计 ...