如何理解递归
-
什么场景会用到递归 最终推荐人\
-
用户 A 推荐用户 B 来注册
-
用户 B 又推荐了用户 C 来注册\
-
给定一个用户 ID,如何查找这个用户的“最终推荐人”?\
-
-
应用广泛的算法:DFS 深度优先搜索、前中后序二叉树遍历等等\
- 去的过程叫“递”,回来的过程叫“归”
递归需要满足的三个条件
-
一个问题可以分解成几个问题:将问题拆分成更小的问题\
-
分解后的问题除了数据规模不同,求解思路一致:解决问题手段一致\
-
存在递归终止条件:存在递归终止条件\
如何编写递归代码
-
关键:写出递推公式(递归问题的关键) ,找到终止条件(基于推导公式做的)
-
以走台阶为例
-
f(n) = f(n-1)+f(n-2) 划分子问题,由于最后一步必然不同,所以需要相加起来
-
找出终止条件
-
f(1)=1\
-
f(2)=2\
-
-
-
怎么写递归\
-
找到如何将大问题分解为小问题的规律,可能是不止一种的小问题
-
并且基于此写出递推公式\
-
再推敲终止条件(最后找边界)\
-
-
总结\
-
计算机擅长做重复的事情,所以递归正合它的胃口\
-
人脑更喜欢平铺直叙的思维方式,不可能想清楚递归的每一步执行\
-
抽象成递推公式,不去想调用关系,更不要想分解每个步骤
-
递归代码要警惕堆栈溢出
-
栈:当函数调用执行完后才出栈,如果递归不结束,调用栈很深的话会有堆栈溢出的风险
-
避免堆栈溢出
- 调用深度(是可以确定的深度情况下,10,50)超过一定深度则不向下递归
-
缺点:
- 最大允许的递归深度跟当前线程剩余的栈空间大小有关,事先无法计算
递归代码要警惕重复计算
- 以爬楼梯来看,会重复计算子问题
- 可以定义散列表来保存已经计算过的结果
- 时间复杂度为O(n)
\
怎么将递归代码改写为非递归代码?
- 递归代码的优点:表达力强,写起来简洁
- 递归代码的缺点:空间复杂度高,有堆栈溢出风险、存在重复计算、过多的函数调用会耗时较多等问题
- 所有的递归都可以改造成非递归方式,但是本质没有改变,还增加了实现的复杂度(手动模拟计算机的入栈和出栈过程)
- 在链表上地柜时需要注意链表是否成环
总结
-
递归是一种非常高效、简洁的编码技巧
-
也存在堆栈溢出、重复计算、函数调用耗时多、空间复杂度高等问题
-
技巧
- 拆分成子问题
- 解决子问题手段一致
- 存在终止条件