递归方法编写指南

递归:“自己调用自己”,每次调用时都会把问题变得更简单一点,直到问题简单到可以直接解决为止。

就像剥洋葱:你先剥开一层,发现里面结构跟整个洋葱一样,于是继续剥这一层,直到剥到最中心最小的一片,然后从内向外,把每一层的结果组合起来,得到完整的答案。

写递归时,必须明确两件事


1. 递归结束条件(Base Case)
  • 作用:告诉递归“什么时候该停了”,避免无限循环。
  • 比喻:像你拆套娃时,发现“这个套娃不能再拆了”(比如最小的那个),就停止。
  • 例子
    • 计算阶乘 n! 时,结束条件是 0! = 11! = 1
    • 遍历文件夹时,结束条件是“当前文件夹没有子文件夹”。

n!称为阶乘,表示从1乘到n,就像一步步爬阶梯一样,一步步乘上去。
n! = n ×\times× n−1{n-1}n1 ×\times× n−2{n-2}n2 ×\times× n−3{n-3}n3 ×\times× …… ×\times× 3 ×\times× 222 ×\times× 111

def factorial(n):
    if n == 0:  # 结束条件
        return 1
    # 否则继续递归...

如果没有结束条件:程序会一直递归下去,直到崩溃。

类似:
“从前有座山,山上有座道观,道观里有一个老道长和小道长在讲故事,他们在讲什么呢?他们讲的是
‘从前有座山,山上有座道观,道观里有一个老道长和小道长在讲故事,他们在讲什么呢?他们讲的是
“从前……”’”


2. 递归规律(Recursive Case)
  • 作用:定义“如何把问题拆小”,让每次递归都向结束条件靠近。
  • 比喻:像你每次拆开一个盒子后,对里面的更小的盒子重复同样的拆解操作。
  • 关键点
    • 问题规模必须减小:比如计算 n! 时,递归调用 factorial(n-1)
    • 相信递归能工作:不要深入跟踪每次调用,假设子问题能正确解决(类似“相信下一层套娃能被正确拆开”)。
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)  # 问题规模减小(n-1)

完整例子:计算阶乘

def factorial(n):
    # 1. 结束条件
    if n == 0:
        return 1
    # 2. 递归规律:n! = n * (n-1)!
    else:
        return n * factorial(n - 1)

执行流程(以 factorial(3) 为例):

  1. 3! = 3 * factorial(2)
  2. 2! = 2 * factorial(1)
  3. 1! = 1 * factorial(0)
  4. 0! = 1(触发结束条件,开始返回结果)
  5. 逐层返回:1 * 1 = 12 * 1 = 23 * 2 = 6

常见问题

  1. 忘记结束条件:无限递归,最终崩溃(Stack Overflow)。
  2. 问题没拆小:比如递归调用时参数没变化,导致无限循环。
  3. 效率问题:递归可能有重复计算(如斐波那契数列),可用记忆化优化。

斐波那契数列:F0=0,F1=1F_0 = 0, F_1 = 1F0=0,F1=1
n≥2,Fn=Fn−1+Fn−2n \geq 2, F_n = F_{n - 1} + F_{n - 2}n2,Fn=Fn1+Fn2

记忆化优化:
在递归函数中,一个函数会不断调用自己,把大问题拆成小问题。但有些小问题会被反复计算很多次,数据越大,重复计算就越多,浪费大量时间。
记忆化优化,就是给递归函数“做笔记”。
每次算出一个子问题的结果,就把它记下来(比如存到一个数组或字典里)。
下次再遇到同样的问题,直接查“笔记”,不用重新算。


总结

  • 结束条件:递归的“底线”,避免无限循环。
  • 递归规律:把大问题拆成小问题,并假设子问题能解决。
  • 信任递归:像相信“下一层盒子会被正确拆解”一样,不要过度纠结细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值