1899 字
9 分钟
Python 递归

Python 递归入门#

学习目标#

  • 理解递归的核心思想:自己调用自己
  • 掌握递归三要素:终止条件、递归关系、递归调用
  • 能通过递归解决阶乘和斐波那契数列等经典问题
  • 了解递归的优缺点,知道什么时候该用、什么时候不该用

一、什么是递归?#

想象你站在两面相对的镜子之间,会看到无穷无尽的镜像。这就是递归的直观感受:一个函数在内部调用它自己。

flowchart TD
    A["n = 5<br/>调用 fac(5)"] --> B["n = 4<br/>调用 fac(4)"]
    B --> C["n = 3<br/>调用 fac(3)"]
    C --> D["n = 2<br/>调用 fac(2)"]
    D --> E["n = 1<br/>碰到终止条件!"]
    E --> F["返回 1"]
    F --> G["返回 2"]
    G --> H["返回 6"]
    H --> I["返回 24"]
    I --> J["返回 120"]

    style A fill:#ffe599,stroke:#333
    style E fill:#d9ead3,stroke:#333
    style J fill:#9ccff9,stroke:#333

二、递归的三要素#

写对递归,必须把下面三件事想清楚:

要素含义以阶乘为例
① 终止条件(base case)什么时候停下来?n == 1 时返回 1
② 递归关系(recurrence)大问题和小问题是什么关系?fac(n) = n × fac(n-1)
③ 递归调用(recursive call)怎么用更小的参数调用自己?return n * fac(n-1)

递归,到底”递”了什么,“归”了什么?#

把”递归”两个字拆开看:

  • 递:把问题传递下去—— “我算不出来,但我可以把问题缩小一号,交给下一个我”
  • 归:把答案归还上来—— “最底层的我算出来了,一层一层把结果传回去”

打个比方:你排在一列长队的最后面,想知道自己是第几个人。你不会从头数,而是——

拍前面人的肩膀:“嘿,你是第几个?” 前面的人也不知道,他又拍更前面的人…… 一直传到第一个人:“我是第 1 个!” 然后答案一路往回传:“我是第 2 个” → “我是第 3 个” → …… → “你是第 10 个!”

flowchart LR
    A["第10人<br/>'我是第几个?'"] -->|"递:往前问"| B["第9人<br/>'我是第几个?'"]
    B -->|"递:往前问"| C["..."]
    C -->|"递:往前问"| D["第1人<br/>'我是第1个!'"]
    D -->|"归:往回传"| C
    C -->|"归:往回传"| B
    B -->|"归:往回传"| A

    style A fill:#ffe599,stroke:#333
    style D fill:#d9ead3,stroke:#333

这个排队问人的过程,就是递归——

排队场景对应递归
“你是第几个?”函数调用自己
第一个人说”我是第1个”终止条件(base case)
“我是前面那个的编号 +1”递归关系
一路往回传答案返回值逐层弹栈

💡 选看:递归的底层原理——调用栈

每次函数调用,Python 都会在调用栈上压入一个”栈帧”,记录函数的局部变量和返回地址。递归本质上就是把栈帧一层一层叠上去,触底(终止条件)后再一层一层弹回来。

# 用一个简单例子观察调用栈
def countdown(n):
print(f"进入 countdown({n})") # ← 压栈时打印
if n <= 0:
return
countdown(n - 1)
print(f"退出 countdown({n})") # ← 弹栈时打印
countdown(3)
进入 countdown(3)
进入 countdown(2)
进入 countdown(1)
进入 countdown(0)
退出 countdown(1)
退出 countdown(2)
退出 countdown(3)

三、实战题目#

题目 1:计算阶乘和(1! + 2! + … + n!)#

题目:#

用户输入一个正整数 n,计算 1! + 2! + 3! + … + n! 的值并输出。

讲解:#

这道题可以分为两步:

  1. 写一个递归函数计算阶乘:jiecheng(n)
  2. 累加 1 到 n 的阶乘:jiecheng(1) + jiecheng(2) + ... + jiecheng(n)

阶乘的递归关系:

n!={1(n=1)n×(n−1)!(n>1)n! = \begin{cases} 1 & (n = 1) \\ n \times (n-1)! & (n > 1) \end{cases}

解题思路:#

flowchart LR
    A["输入 n"] --> B["定义递归函数<br/>jiecheng(k)"]
    B --> C{"k == 1 ?"}
    C -- 是 --> D["返回 1"]
    C -- 否 --> E["返回 k × jiecheng(k-1)"]
    D --> F["累加 jiecheng(1) 到 jiecheng(n)"]
    E --> F
    F --> G["输出结果"]

    style B fill:#9ccff9,stroke:#333
    style D fill:#d9ead3,stroke:#333
    style G fill:#ffe599,stroke:#333

代码实现:#

# ── 递归版:求 n 的阶乘 ──
def jiecheng(n):
if n == 1: # 终止条件:1! = 1
return 1
return n * jiecheng(n - 1) # 递归关系:n! = n × (n-1)!
# ── 累加 1! 到 n! ──
n = int(input(""))
k = 0
for j in range(1, n + 1):
k += jiecheng(j)
print(k)
变量作用
jiecheng(n)递归计算 n!
k累加器,存放最终的和
range(1, n+1)从 1 遍历到 n,逐个累加阶乘

运行测试:#

输入计算过程输出
31! + 2! + 3! = 1 + 2 + 69
51! + 2! + 3! + 4! + 5! = 1 + 2 + 6 + 24 + 120153

题目 2:递归计算斐波那契数列第 n 项#

题目:#

使用递归计算斐波那契数列的第 20 个数字。

讲解:#

斐波那契数列的定义:

F(n)={1(n=1 或 n=2)F(n−1)+F(n−2)(n>2)F(n) = \begin{cases} 1 & (n = 1 \text{ 或 } n = 2) \\ F(n-1) + F(n-2) & (n > 2) \end{cases}

数列的前几项:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

斐波那契数列是递归的天然例题——因为它的定义本身就是递归的:每一项等于前两项之和。

flowchart TD
    A["F(5)"] --> B["F(4)"]
    A --> C["F(3)"]
    B --> D["F(3)"]
    B --> E["F(2)"]
    C --> F["F(2)"]
    C --> G["F(1)"]
    D --> H["F(2)"]
    D --> I["F(1)"]
    E --> J["= 1"]
    F --> K["= 1"]
    G --> L["= 1"]
    H --> M["= 1"]
    I --> N["= 1"]

    style A fill:#ffe599,stroke:#333
    style J fill:#d9ead3,stroke:#333
    style K fill:#d9ead3,stroke:#333
    style L fill:#d9ead3,stroke:#333
    style M fill:#d9ead3,stroke:#333
    style N fill:#d9ead3,stroke:#333

解题思路:#

  • 三要素分析:
    • 终止条件:n == 1 或 n == 2 → 返回 1
    • 递归关系:F(n) = F(n-1) + F(n-2)
    • 递归调用:return feibo(n-1) + feibo(n-2)

代码实现:#

# ── 递归版:求斐波那契数列第 n 项 ──
def feibo(n):
if n == 1 or n == 2: # 终止条件
return 1
return feibo(n - 1) + feibo(n - 2) # 递归关系
print(feibo(20))

运行结果:#

6765
输入含义输出
feibo(1)第 1 项1
feibo(2)第 2 项1
feibo(10)第 10 项55
feibo(20)第 20 项6765

四、递归 vs 循环:什么时候用递归?#

对比维度递归循环
代码简洁度简洁、接近数学定义通常代码更长
内存占用每次调用占一个栈帧,可能栈溢出只用一个循环体,内存开销小
执行速度函数调用有开销,略慢直接循环,更快
适合场景树遍历、分治、回溯、数学归纳简单重复操作、线性迭代

斐波那契用递归来计算第 20 项已经要调用 13529 次函数了,求第 50 项怕不是要等到天荒地老……

💡 进阶:用记忆化优化斐波那契

递归求斐波那契最大的问题是重复计算。用一个字典把算过的结果存起来,下次遇到直接取,效率就能从指数级降到线性级:

# ── 记忆化递归:用字典缓存已计算结果 ──
cache = {}
def feibo(n):
if n in cache: # 算过了,直接返回
return cache[n]
if n == 1 or n == 2:
result = 1
else:
result = feibo(n - 1) + feibo(n - 2)
cache[n] = result # 存入缓存
return result
print(feibo(20)) # 6765,但这次快得多
print(feibo(50)) # 12586269025,瞬间出结果

这就是记忆化搜索,也叫自顶向下的动态规划——是递归进阶的重要技巧!


五、总结#

核心要点一句话
什么是递归函数自己调用自己,把大问题拆成小问题
递归三要素终止条件 + 递归关系 + 递归调用,缺一不可
调用栈先进后出,终止条件触底后逐层返回
阶乘递归n! = n × (n-1)!,终止于 1
斐波那契递归F(n) = F(n-1) + F(n-2),终止于 F(1)=F(2)=1
什么时候用问题天然是”自相似”结构时用递归,简单重复用循环
Python 递归
https://mikufans.yuzhiyuan.top/posts/python递归/
作者
遇缘
发布于
2026-05-18
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 139 天

部分内容可能已过时