一、什么是递归
递归听着吓人,其实就是一句话:一个函数在做的过程中,又调用了它自己。
先别想数学,看一个生活例子:数一串珠子,你数到一半让同桌「帮我把剩下的数完」,同桌又让下一个人数——这就是递归的味道。
代码里长这样:
def f():
print('我在干活')
f() # 在函数里又调用了自己
f()
它会不停地打印「我在干活」。因为 f() 里面又调 f(),永远没完——所以递归必须有个「停下来的条件」,不然就成死循环了(后面第九节会看到它怎么报错)。
用循环写「倒计时」,也能得到一样的结果:
i = 3
while i > 0:
print(i)
i -= 1
输出:
3
2
1
什么时候用递归
| 适合递归 | 不适合递归 |
|---|---|
| 问题能拆成「和自己长得一样、但更小」的子问题 | 简单的一次性计算 |
| 比如:阶乘、斐波那契、文件夹遍历、汉诺塔 | 比如:加减乘除、单趟循环 |
起步阶段,先把「递归长什么样、怎么停」搞懂,例子都是故意挑简单的。真正复杂的递归,大二大三才用得上。
二、递归的两个必备条件
写递归函数,脑子里必须同时装两样东西,缺一个都会出错:
| 条件 | 作用 | 忘了会怎样 |
|---|---|---|
| ① 出口(基线条件) | 告诉函数「到这一步就别再调自己了,直接返回」 | 永远调自己 → 层数爆掉报错 |
| ② 每步变小 | 每次调用,问题规模都要比上一次小一点 | 规模不变 → 永远到不了出口 |
用「搬砖」打比方:你搬一块,剩下的让后面的人搬(自己调自己);但每次剩下的砖要比之前少(每步变小);直到只剩 0 块时直接说「搬完了」(出口)。
一个合格递归的骨架
# 示意
def 函数(问题):
if 问题已经小到最简单(出口):
return 最简单答案
把问题拆小一点
return 函数(更小的问题) # 自己调自己,规模更小
这是长相,不是能直接跑的代码。第三、四节用倒计时和阶乘把它变成真代码。
课程思政|工匠精神与严谨求实 · 每退一步,都要离出口更近
写递归要同时想清两件事:什么时候停(出口),以及每次是不是真的更接近那个出口。少一样,函数就会一直往里钻,直到撞上 RecursionError。做事也一样,光有目标不够,还得确认每一步都在靠近它。给自己定计划时不妨问一句:这一周我做的,是让离目标更近了一点,还是原地打转?
三、第一个例子:倒计时
倒计时是最好懂的递归:从 n 数到 1。
def countdown(n):
if n <= 0: # 出口:数到 0 或更小就停
return
print(n)
countdown(n - 1) # 自己调自己,而且 n 比上次小 1
countdown(3)
输出:
3
2
1
走一遍:countdown(3) 打印 3,再调 countdown(2);打印 2,调 countdown(1);打印 1,调 countdown(0);到 0 触发出口 return,结束。
n <= 0 是出口:告诉函数「别再往里钻了」。countdown(n - 1) 保证每次都离出口更近。两个零件齐了,递归才安全。如果忘了出口会怎样
def countdown(n):
print(n)
countdown(n - 1) # 没有出口!
countdown(3)
它会一直打印下去,直到 Python 受不了报错(详见第九节)。所以出口是递归的命门。
四、递归怎么走(调用栈)
递归最反直觉的是:它「一层层往里钻,再一层层往外退」。用一个调用栈来理解——每调一次,就压一层;返回时,再弹出来。
以 fact(3) 为例(阶乘函数下一节写,这里先借它看过程):
| 动作 | 栈里有什么 | 在做的事 |
|---|---|---|
| 调 fact(3) | fact(3) | 3 不是 1,算 3 × fact(2),先去算 fact(2) |
| 调 fact(2) | fact(3) → fact(2) | 2 不是 1,算 2 × fact(1),先去算 fact(1) |
| 调 fact(1) | fact(3) → fact(2) → fact(1) | 1 触发出口,返回 1 |
| fact(1) 返回 | fact(3) → fact(2) | fact(2) 拿到 1,算 2 × 1 = 2,返回 2 |
| fact(2) 返回 | fact(3) | fact(3) 拿到 2,算 3 × 2 = 6,返回 6 |
看出来了吗:「进去」和「出来」是反着的。先进去的最后出来(像叠盘子)。每一层都「等」着里面那层先给结果。
def fact(n):
if n <= 1:
return 1
return n * fact(n - 1)
print(fact(3))
输出 6。这正是上面表格最后的结果。
fact(n) 时,直接相信 fact(n-1) 会返回正确答案,你只负责把它和 n 乘起来。五、阶乘与累加
阶乘:n! = n × (n-1) × … × 1
阶乘的定义天生就是递归的:n! = n × (n-1)!,而 1! = 1 就是出口。
def fact(n):
if n <= 1: # 出口
return 1
return n * fact(n - 1) # 自己调自己,规模更小
print(fact(5))
print(fact(1))
输出:
120
1
累加:1 + 2 + … + n
和阶乘一个套路,只是把「乘」换成「加」:
def sum_to(n):
if n == 1: # 出口
return 1
return n + sum_to(n - 1)
print(sum_to(5))
输出 15(也就是 5+4+3+2+1)。
递归和循环的对照
# 用循环写累加,更省内存
total = 0
for i in range(1, 6):
total += i
print(total)
输出也是 15。同样的结果,循环写得也不难——这正是下一节要聊的:递归能做的,循环基本都能做。
def my_max(lst):
if len(lst) == 1:
return lst[0]
m = my_max(lst[1:])
return lst[0] if lst[0] > m else m
print(my_max([3, 9, 2, 7]))
输出 9。
课程思政|科技自立自强 · 大难题,拆着拆着就小了
阶乘 n! = n × (n-1)! 天生就是递归的:把一个大问题,拆成一个「当前这一步」加上一个「更小、但长得一模一样的同类问题」。关键技术被人卡脖子,也是这么一点点攻下来的——没有谁一口气拿下整条产业链,靠的都是把大目标拆成一个个能落地的子问题,逐个解决。学会这手拆解,比记住一行代码值钱得多。
六、兔子生小兔:斐波那契的由来
斐波那契数列不是凭空想出来的,它最早来自一道数兔子的题。1202 年,意大利数学家斐波那契在《算盘全书》里问了这样一个问题:
题目:一对小兔子,出生后长一个月就能长大,从第 3 个月起每个月都会生出一对小兔子。假设兔子永远不死,问:n 个月后一共有多少对兔子?
光看题面容易绕晕,我们每个月记一笔账(第 1 个月只有刚买回来的那对小兔子):
| 月份 | 幼兔(下个月才生) | 成年兔(本月会生) | 总对数 |
|---|---|---|---|
| 第 1 个月 | 1 | 0 | 1 |
| 第 2 个月 | 0 | 1 | 1 |
| 第 3 个月 | 1 | 1 | 2 |
| 第 4 个月 | 1 | 2 | 3 |
| 第 5 个月 | 2 | 3 | 5 |
| 第 6 个月 | 3 | 5 | 8 |
| 第 7 个月 | 5 | 8 | 13 |
| 第 8 个月 | 8 | 13 | 21 |
把每月的「总对数」单独抽出来看:1, 1, 2, 3, 5, 8, 13, 21 …——这正是斐波那契数列。规律就藏在记账的两条规则里:
- 上个月的兔子这个月一只都没少,全部保留:这部分就是第 n−1 个月的总数;
- 本月新生的小兔子,来自上上个月就已经存在的兔子——它们到这个月全都成年了,每对生一对:这部分就是第 n−2 个月的总数。
设 f(n) 表示第 n 个月的总对数,于是:
f(n) = f(n-1) + f(n-2)(n >= 3 时成立),出口是 f(1) = f(2) = 1——大问题由两个更小的同类问题拼出来,这正是递归的形状。照着翻译成 Python:
def rabbit(n):
if n == 1 or n == 2: # 出口:前两个月都是 1 对
return 1
return rabbit(n - 1) + rabbit(n - 2) # 上月保留的 + 本月新生的
for m in range(1, 9):
print('第', m, '个月:', rabbit(m), '对')
第 1 个月: 1 对
第 2 个月: 1 对
第 3 个月: 2 对
第 4 个月: 3 对
第 5 个月: 5 对
第 6 个月: 8 对
第 7 个月: 13 对
第 8 个月: 21 对
输出和上面的账本逐月对得上。「先手算小规模、再用程序验证」,这道题给了我们一个完整的示范。注意出口有两个(n == 1 和 n == 2):如果只写 n == 1,rabbit(2) 会继续拆成 rabbit(1) 和 rabbit(0),而 n 每次减 1 或减 2,rabbit(0)、rabbit(-1) 就一路变成负数、永远绕开出口,最后报 RecursionError。另外,这个朴素的递归写法很慢,慢在哪里、怎么补救,见第十一节的坑 4。
课程思政|工匠精神与严谨求实 · 先算小账,再信程序
这道题流传了八百多年,靠的不是拍脑袋,而是一个月一个月老老实实记出来的账。写递归也一样:先手算前几项、列一张小表,再拿程序输出和账本逐项核对。用数据说话、拿事实验证是工程师最朴素的严谨——程序跑通了不等于对了,和真实记录对上了才算数。
七、几个经典案例
1. 斐波那契数列
数列:1, 1, 2, 3, 5, 8, 13 … 规则是「每一项等于前两项之和」,开头两项都是 1。写成递归:
def fib(n):
if n <= 1: # 前两项都是出口
return n
return fib(n - 1) + fib(n - 2)
print(fib(10))
输出 55(第 10 项是 55)。注意它同时调了自己两次,这正是它慢的原因(见第十一节)。
2. 最大公约数(辗转相除法)
def gcd(a, b):
if b == 0: # 出口:余数为 0
return a
return gcd(b, a % b) # 用「除数、余数」缩规模
print(gcd(12, 18))
print(gcd(7, 5))
输出 6、1。12 和 18 的最大公约数是 6;7 和 5 互质,最大公约数是 1。
3. 汉诺塔
把 n 个盘子从 A 柱移到 C 柱,每次只能挪一个,且大盘不能压小盘。递归解法是「先把上面 n-1 个挪到 B,再把最大的挪到 C,最后把 n-1 个从 B 挪到 C」:
def hanoi(n, a, b, c):
if n == 1: # 出口:只剩一个盘,直接挪
print(a, '->', c)
return
hanoi(n - 1, a, c, b) # 上面 n-1 个:A -> B
print(a, '->', c) # 最大的那个:A -> C
hanoi(n - 1, b, a, c) # 上面 n-1 个:B -> C
hanoi(3, 'A', 'B', 'C')
输出(每一步是「从哪根柱挪到哪根柱」):
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C
4. 字符串反转
def rev(s):
if len(s) <= 1: # 出口:空串或单字符直接返回
return s
return rev(s[1:]) + s[0] # 把第一个字符丢到最后
print(rev('奶茶真好喝'))
print(rev('今天不减肥'))
输出:
喝好真茶奶
肥减不天今
5. 列表求和
def lsum(nums):
if not nums: # 出口:空列表,和为 0
return 0
return nums[0] + lsum(nums[1:])
print(lsum([15, 8, 22]))
输出 45——奶茶 15 元 + 柠檬水 8 元 + 蛋糕 22 元的购物车总价。
顺手再看一个同样套路的:求 a 的 b 次方,出口是 b == 0 时返回 1:
def power(a, b):
if b == 0:
return 1
return a * power(a, b - 1)
print(power(2, 5))
输出 32。
6. 猴子吃桃
一道流传很广的趣味题:一只猴子摘了一堆桃子,每天都吃掉剩下的一半再多啃一个,到第 10 天想吃时发现只剩 1 个。问:第一天一共摘了多少个?
正着算很费劲,倒着推却非常简单:只要知道「今天的数量」,就能算回「昨天的数量」——昨天 = (今天 + 1) × 2。这正是递归的形状:第 10 天是出口(1 个),每一天都由「下一天」推出来。
def peach(day):
if day == 10: # 出口:第 10 天只剩 1 个
return 1
return (peach(day + 1) + 1) * 2 # 昨天 = (今天 + 1) × 2
print(peach(1))
输出 1534——第一天竟然摘了 1534 个桃子。
| 天数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| 桃子数 | 1534 | 766 | 382 | 190 | 94 | 46 | 22 | 10 | 4 | 1 |
return,递归就出来了。7. 青蛙跳台阶
一只青蛙要跳上 n 级台阶,每次只能跳 1 级或 2 级,问一共有多少种跳法?
站在终点倒着看:最后一次要么从第 n-1 级跳 1 级上来,要么从第 n-2 级跳 2 级上来,两种情况互不重叠,所以 jump(n) = jump(n-1) + jump(n-2)。出口是最简单的两级:1 阶只有 1 种跳法,2 阶有 2 种。
def jump(n):
if n <= 2: # 出口:1 阶 1 种,2 阶 2 种
return n
return jump(n - 1) + jump(n - 2) # 最后一步:跳 1 阶或跳 2 阶
print(jump(5))
print(jump(10))
输出 8 和 89。眼熟吗?1, 2, 3, 5, 8, 13…这就是斐波那契数列换了一身故事——很多看似不同的问题,递归式其实是同一个。
8. 剥洋葱:拍平嵌套列表
列表里还能套列表,像俄罗斯套娃:[1, [2, 3], [4, [5, 6]]]。想把所有数字一层层剥出来拍成一维,用循环写会非常别扭,而递归天然擅长这种嵌套结构:碰到盒子就打开(调自己),碰到单个数字就收下。
def flat(xs):
result = []
for x in xs:
if isinstance(x, list): # 还是盒子:打开它,递归处理
result += flat(x)
else: # 是单个数字:直接收下
result.append(x)
return result
data = ['奶茶', ['加珍珠', '三分糖'], ['加椰果', ['换燕麦奶']]]
print(flat(data))
输出 ['奶茶', '加珍珠', '三分糖', '加椰果', '换燕麦奶']。这个案例的出口不再是「数量变小」,而是「嵌套深度变浅」:每递归一层,剥掉一层盒子,最深处必然是最普通的那个元素。文件目录、网页标签这类「套娃结构」,用的都是同一招。
AI 助手小贴士|经典案例值得让 AI 把「看不见的过程」画出来。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:用文字画出 3 层汉诺塔的移动步骤和递归调用树,并解释为什么最少要 2 的 n 次方减 1 步
八、递归 vs 循环
老实说:前面这些递归,用循环都能写,而且循环通常更快、更省内存。
| 递归 | 循环 | |
|---|---|---|
| 写法 | 短、像数学定义,好懂 | 稍长,但要自己管计数器 |
| 速度 | 慢(每次调用都有开销) | 快 |
| 内存 | 每层都压栈,深了会爆 | 基本不额外占内存 |
| 适合 | 问题天然递归(树、目录、分治) | 绝大多数日常计算 |
同一个「列表求和」,循环版:
nums = [15, 8, 22]
total = 0
for x in nums:
total += x
print(total)
输出 45,和递归版一模一样,但循环不用压栈、不会爆层数。
def show(s):
if not s:
return
print(s[0])
show(s[1:])
show('hi')
输出:
h
i
课程思政|诚信与职业操守 · 别为了递归而递归
前面那些递归,用循环都能写,而且通常更快、更省内存。递归真正的用武之地,是那些「结构本身就一层套一层」的问题,比如遍历文件夹。选工具要看问题合不合适,不看它显得高不高级。硬把简单的活写成花哨的样子,看着厉害,其实既不诚实也不专业——对自己实在一点,什么合用就用什么。
九、忘记出口会怎样(RecursionError)
递归如果忘了写出口(或出口永远触发不了),函数会一直调自己,层数越压越深,直到撞上 Python 的上限,抛出 RecursionError。
def f(n):
return f(n - 1) # 只有调用,没有出口
f(5)
RecursionError: maximum recursion depth exceeded
这就是真实的报错原文:递归层数超过了上限。下面这种「规模没变小」也是同样的错:
def f(n):
f(n) # 传进去的还是 n,永远到不了出口
f(3)
RecursionError: maximum recursion depth exceeded
两种不同的「忘出口」写法,报错都是同一句 RecursionError: maximum recursion depth exceeded。
RecursionError,第一反应就是查:出口写了吗?每次调用规模真的变小了吗?十有八九是这两处漏了。循环里的死循环靠 Ctrl+C 停,递归的死循环只能改代码。sys.setrecursionlimit() 调大,但那只是推迟崩溃,真正该做的是让递归能正常结束,而不是硬撑层数。十、十个基础案例
下面十个例子都很短,建议每个都亲手敲一遍、跑一遍。它们覆盖了递归最常见的写法。
1. 倒计时(从 n 数到 1)
def countdown(n):
if n <= 0:
return
print(n)
countdown(n - 1)
countdown(3)
输出:
3
2
1
2. 阶乘
def fact(n):
if n <= 1:
return 1
return n * fact(n - 1)
print(fact(0))
print(fact(6))
输出:
1
720
3. 求 1 加到 n 的和
def sum_to(n):
if n == 1:
return 1
return n + sum_to(n - 1)
print(sum_to(5))
输出 15。
4. 求购物清单总价
def lsum(nums):
if not nums:
return 0
return nums[0] + lsum(nums[1:])
print(lsum([15, 8, 22]))
输出 45:奶茶 15 + 柠檬水 8 + 蛋糕 22。
5. 求字符串长度
def my_len(s):
if s == '':
return 0
return 1 + my_len(s[1:])
print(my_len('珍珠奶茶'))
print(my_len('hello'))
输出:
4
5
6. 反转字符串
def rev(s):
if len(s) <= 1:
return s
return rev(s[1:]) + s[0]
print(rev('奶茶真好喝'))
输出 喝好真茶奶。
7. 判断回文串(正着读反着读一样)
def is_pal(s):
if len(s) <= 1:
return True
if s[0] != s[-1]:
return False
return is_pal(s[1:-1])
print(is_pal('上海自来水来自海上'))
print(is_pal('abc'))
输出:
True
False
8. 求 2 的 n 次方
def pow2(n):
if n == 0:
return 1
return 2 * pow2(n - 1)
print(pow2(3))
print(pow2(0))
输出:
8
1
9. 求最大公约数
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
print(gcd(12, 18))
输出 6。
10. 十进制转二进制(字符串)
def to_bin(n):
if n < 2:
return str(n)
return to_bin(n // 2) + str(n % 2)
print(to_bin(10))
print(to_bin(13))
输出:
1010
1101
十一、常见坑
坑 1:忘写出口 → RecursionError
def f(n):
return f(n - 1) # 没出口
f(5)
RecursionError: maximum recursion depth exceeded
每次调用都压一层栈,超过上限就崩。先写出口再写递归体。
坑 2:规模没变小,永远到不了出口
def f(n):
f(n) # 传的还是 n,没缩小
f(3)
RecursionError: maximum recursion depth exceeded
必须让参数朝出口方向靠近,比如 n - 1、lst[1:]、n // 2。
坑 3:递归里忘写 return
递归体里调了自己,却忘了 return 把结果交出来,外面拿到的是 None:
def f(n):
if n == 0:
return 0
else:
f(n - 1) + 1 # 算了却没 return
print(f(3))
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
f(n-1) + 1 算出来的结果被丢掉了,函数默认返回 None,None + 1 直接报类型错。把这一行改成 return f(n - 1) + 1 即可。
坑 4:朴素斐波那契慢得离谱
前面那个 fib(n) 每次都「双调用」,会大量重复计算。算 fib(20) 就要调自己两万多次:
count = 0
def fib(n):
global count
count += 1
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
print(fib(20), count)
输出 6765 21891——为了算第 20 项,函数被调用了 21891 次!同样的活,循环只要 20 次。所以真要算斐波那契,用循环或记住结果(记忆化),别用这个朴素递归。
十二、报错手册
递归相关最常见的报错就这两类,照抄本机解释器原文。
def f(n):
return f(n - 1)
f(5)
RecursionError: maximum recursion depth exceeded
原因:没有出口,或出口触发不了,层数超过上限。检查「出口条件」和「每次有没有缩小规模」。
def f(n):
f(n)
f(3)
RecursionError: maximum recursion depth exceeded
原因:传给自己的还是 n,规模一点没变小,永远到不了出口。改成 f(n - 1) 之类。
def f(n):
if n == 0:
return 0
else:
f(n - 1) + 1
print(f(3))
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
原因:递归体忘了 return,函数返回 None,后面拿 None 做加法。补上 return。
def fact(n):
return n * fact(n - 1) # 漏了出口
print(fact(3))
RecursionError: maximum recursion depth exceeded
原因:fact 没有处理 n <= 1 的出口,会一路乘到负数还不停。补上 if n <= 1: return 1。
AI 助手小贴士|递归写错往往不报错、只是算不对,练改错眼力。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:出 3 道递归改错题:分别缺出口、没缩小问题规模、返回值没传回去,让我找出问题并修复
十三、递归思维小结
学了这么多例子,其实写递归就三步,记住就不慌:
| 步骤 | 要想什么 | 例子(数 1~n 的和) |
|---|---|---|
| ① 找出口 | 最简单到不能再小的情况返回什么 | n == 1 时返回 1 |
| ② 信里面 | 假设「更小的那次调用」已经算对了 | 相信 sum_to(n-1) 会给正确结果 |
| ③ 拼当前 | 用这一步 + 里面的结果,凑出答案 | 返回 n + sum_to(n-1) |
用三步法写一个:数一下列表里偶数的个数
def count_even(lst):
if not lst: # ① 出口:空列表,偶数 0 个
return 0
cur = 1 if lst[0] % 2 == 0 else 0 # 当前这个数算 1 或 0
return cur + count_even(lst[1:]) # ②+③ 当前 + 更小的列表
print(count_even([1, 2, 3, 4, 5, 6]))
输出 3(2、4、6 共三个偶数)。
新手最容易卡在哪
| 卡住的地方 | 怎么破 |
|---|---|
| 不知道出口写什么 | 先想「最小的输入」:空列表?n=1?长度为 1 的字符串? |
| 不敢写「自己调自己」 | 先假设它已经能用了,你只管把它和当前这步拼起来 |
| 写出来死循环 | 检查每次调用的参数有没有朝出口方向变小 |
AI 助手小贴士|递归最难在「看不见调用过程」。让 AI 把调用树一层层画出来,画两棵树就通了。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:用文字画出 factorial(4) 的递归调用树:每一层传了什么参数、什么时候到底、返回值怎么一层层带回去
十四、随堂自测(10 题)
点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。
1下面哪句话最准确地描述了「递归」?
- A. 一个函数自己调用自己
- B. 函数里再调用别的函数
- C. 把代码写在一个循环里
- D. 函数只能被调用一次
2一个正确的递归函数,必须同时具备哪两个条件?
- A.
循环和列表 - B.
出口(基线条件)和每步缩小规模 - C.
两个参数 - D.
return 和 print
3fact(n) 里写成 return n * fact(n - 1),这一步主要体现了递归的哪一点?
- A. 函数在逃避计算
- B. 把问题拆成更小的同类问题(n 比 n-1 小)
- C. 在给变量改名
- D. 两个函数互相调用
fact(n) 借助 fact(n-1) 算结果,正是「当前这一步 + 规模更小的同类问题」。配合 n<=1 的出口,递归才成立。4递归函数忘了写出口,运行时会怎样?
- A.
输出 0 - B.
正常结束 - C.
打印 None - D. 抛出 RecursionError
RecursionError: maximum recursion depth exceeded。5关于「递归」和「循环」,下面哪种说法更稳妥?
- A. 递归比循环快
- B. 循环通常更快、更省内存,能用循环就别用递归
- C. 递归永远不会出错
- D. 循环不能做累加
6程序运行时提示 RecursionError: maximum recursion depth exceeded,首先该查什么?
- A. 是不是少写了 print
- B. 是不是把变量名拼错了
- C. 出口写没写、规模有没有缩小
- D. 是不是没导入库
7下面哪个是递归的经典应用场景?
- A. 汉诺塔(盘子一层套一层地挪)
- B. 简单的 1+2+3 累加
- C. 单次 if 判断
- D. 打印一行文字
8递归函数里调了自己却忘了写 return,外部拿到的结果通常是?
- A.
正确的值 - B.
0 - C.
报错 SyntaxError - D.
None
None。若后面还拿它做运算,会进一步报 TypeError: ...NoneType...。记得把递归结果 return 出来。9朴素递归版斐波那契(fib(n)=fib(n-1)+fib(n-2))为什么慢?
- A. 因为 Python 不支持加法
- B. 因为函数名太长
- C. 因为大量子问题被重复计算
- D. 因为没用列表
fib 都要双调用,导致同样的子问题被反复算(算 fib(20) 要调用自己两万多次)。真要算请用循环或加缓存,别用朴素递归。10老师给的递归使用建议,最贴近下面哪句?
- A. 所有循环都该改成递归
- B. 能用循环就别用递归,递归留给天然递归的问题
- C. 递归层数越多越好
- D. 递归不需要出口