前言递归,是一个非常重要的概念,也是面试中非常喜欢考的 。因为它不但能考察一个程序员的算法功底,还能很好的考察对 时间空间复杂度 的理解和分析 。
本文只讲一题,也是几乎所有算法书讲递归的第一题,但力争讲出花来,在这里分享四点不一样的角度,让你有不同的收获 。
- 时空复杂度 的详细分析
- 识别并 简化 递归过程中的 重复 运算
- 披上羊皮的狼
- 适当炫技助我拿到第一份工作
那么递归的 实质 是什么?
答:<span style="color:blue">递归的实质是能够把一个大问题分解成比它小点的问题,然后我们拿到了小问题的解,就可以用小问题的解去构造大问题的解 。</span>
那小问题的解是如何得到的?
答:用再小一号的问题的解构造出来的,小到不能再小的时候就是到了零号问题的时候,也就是 base case 了 。
![一文学懂递归和动态规划](http://img.jiangsulong.com/220422/0455563138-0.jpg)
文章插图
那么总结一下递归的三个步骤:Base case:就是递归的零号问题,也是递归的终点,走到最小的那个问题,能够直接给出结果,不必再往下走了,否则,就会成死循环;
拆解:每一层的问题都要比上一层的小,不断缩小问题的 size,才能从大到小到 base case;
组合:得到了小问题的解,还要知道如何才能构造出大问题的解 。
所以每道递归题,我们按照这三个步骤来分析,把这三个问题搞清楚,代码就很容易写了 。
斐波那契数列这题虽是老生常谈了,但相信我这里分享的一定会让你有其他收获 。
题目描述斐波那契数列是一位意大利的数学家,他闲着没事去研究兔子繁殖的过程,研究着就发现,可以写成这么一个序列:1,1,2,3,5,8,13,21… 也就是每个数等于它前两个数之和 。那么给你第 n 个数,问 F(n) 是多少 。
解析用数学公式表示很简单:
$$f(n) = f(n-1) + f(n-2)$$
代码也很简单,用我们刚总结的三步:
- base case: f(0) = 0, f(1) = 1.
- 分解:f(n-1), f(n-2)
- 组合:f(n) = f(n-1) + f(n-2)
class Solution {public int fib(int N) {if (N == 0) {return 0;} else if (N == 1) {return 1;}return fib(N-1) + fib(N-2);}}
但是这种解法 Leetcode 给出的速度经验只比 15% 的答案快,因为,它的时间复杂度实在是太高了!![一文学懂递归和动态规划](http://img.jiangsulong.com/220422/04555C216-1.jpg)
文章插图
过程分析这就是我想分享的第一点,如何去分析递归的过程 。
首先我们把这颗 Recursion Tree 画出来,比如我们把 F(5) 的递归树画出来:
![一文学懂递归和动态规划](http://img.jiangsulong.com/220422/0455563W9-2.jpg)
文章插图
那实际的执行路线是怎样的?
首先是沿着最左边这条线一路到底:F(5) → F(4) → F(3) → F(2) → F(1),好了终于有个 base case 可以返回 F(1) = 1 了,然后返回到 F(2) 这一层,再往下走,就是 F(0),又触底反弹,回到 F(2),得到 F(2) = 1+0 =1 的结果,把这个结果返回给 F(3),然后再到 F(1),拿到结果后再返回 F(3) 得到 F(3) = 左 + 右 = 2,再把这个结果返上去...
这种方式本质上是由我们计算机的 冯诺伊曼体系 造就的,目前 一个 CPU 一个核在某一时间只能执行一条指令 ,所以不能 F(3) 和 F(4) 一起进行了,一定是先执行了 F(4) (本代码把 fib(N-1) 放在前面),再去执行 F(3).
我们在 IDE 里 debug 就可以看到栈里面的情况:这里确实是先走的最左边这条线路,一共有 5 层,然后再一层层往上返回 。
![一文学懂递归和动态规划](http://img.jiangsulong.com/220422/04555A2H-3.jpg)
文章插图
没看懂的小伙伴可以看视频讲解哦~
完整版视频还请大家移步 B 站,搜索「田小齐」「递归」,即可看到我的视频讲解 。
<video src=https://www.isolves.com/it/cxkf/sf/2020-09-21/2.%20%E5%85%AC%E4%BC%97%E5%8F%B7/5%20Recursion%20-%20Fib/%E5%8E%8B%E6%A0%88%E8%BF%87%E7%A8%8B%E8%AF%A6%E8%A7%A3.MP4">
时间复杂度分析如何评价一个算法的好坏?
很多问题都有多种解法,毕竟条条大路通罗马 。但如何评价每种方法的优劣,我们一般是用 大 O 表达式 来衡量 时间和空间 复杂度 。
时间复杂度:随着自变量的增长,所需时间的增长情况 。
推荐阅读
- 一款随机代理小工具,github开源
- 虚拟机XP忘记用户密码,一招搞定
- 一套就能用的短视频脚本模板,谁套谁火
- 上班族玩自媒体,一天300,推荐这3个零基础可做的领域
- 元朝统一的原因和意义 元朝巩固统治的原因
- 项羽在破釜沉舟中他是一个什么样的人 项羽破釜沉舟成功的原因
- Java如何判断一个字符串中某个字符出现的次数?
- 常用的Websocket技术一览
- 中医治疗神经衰弱
- 咽喉炎的治疗