用一个「俄罗斯套娃」的故事,配上超级简单的代码,帮你秒懂递归!
一、递归是什么?用套娃解释
故事:
你收到一个神秘盒子,里面还有盒子,再打开还有盒子... 直到第 5 层盒子,里面有一颗糖果!
规则:
-
打开盒子后,发现里面还有盒子 → 继续打开(重复相同动作)。
-
直到发现糖果 → 停止(终止条件)。
这就是递归:自己调用自己,直到满足终止条件。
二、递归的核心要素
-
终止条件(糖果):
- 必须有一个明确的终点,否则会无限循环(栈溢出)。
-
自我调用(打开下一层盒子):
- 每次调用时,问题规模变小(如盒子层数减少)。
三、经典递归:计算阶乘(n!)
问题:
计算 5!
(5 的阶乘)。
递归解法:
-
定义:
n! = n × (n-1)!
。 -
终止条件:
0! = 1
。
过程:
plaintext
5! = 5 × 4!
4! = 4 × 3!
3! = 3 × 2!
2! = 2 × 1!
1! = 1 × 0!
0! = 1 ← 终止条件
Java 代码:
java
public class Factorial {
public static int factorial(int n) {
// 终止条件:0! = 1
if (n == 0) {
return 1;
}
// 自我调用:n! = n × (n-1)!
return n * factorial(n - 1);
}
public static void main(String[] args) {
int result = factorial(5);
System.out.println("5! = " + result); // 输出: 120
}
}
四、递归 vs 循环:用爬楼梯对比
问题:
你要爬 3 级楼梯,每次可以走 1 步。有多少种走法?
1. 递归解法:
java
public static int climbStairs(int n) {
// 终止条件:只剩 0 级,只有 1 种走法(不动)
if (n == 0) {
return 1;
}
// 自我调用:走 1 步后,剩下 n-1 级
return climbStairs(n - 1);
}
2. 循环解法:
java
public static int climbStairsLoop(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
// 循环计算:1 × 1 × 1 × ... × 1
}
return result;
}
对比:
递归 | 循环 |
---|---|
代码简洁,逻辑清晰 | 性能更高,避免栈溢出 |
适合问题可拆解的场景 | 适合简单重复的任务 |
五、递归的执行过程:用函数调用栈理解
故事:
你是餐厅服务员,接到一个订单:
-
顾客 A 点了汉堡 → 你记录下来(压入栈)。
-
做汉堡时,发现需要番茄酱 → 你让厨师 B 去拿(调用新函数)。
-
厨师 B 拿番茄酱时,发现需要开瓶器 → 让厨师 C 去拿(嵌套调用)。
-
厨师 C 拿到开瓶器 → 返回给 B(函数返回)。
-
厨师 B 打开番茄酱 → 返回给你(函数返回)。
-
你完成汉堡 → 交给顾客(最终返回)。
函数调用栈:
plaintext
main() → climbStairs(3) → climbStairs(2) → climbStairs(1) → climbStairs(0)
← ← ← ←
六、进阶递归:斐波那契数列
问题:
计算斐波那契数列的第 5 项:1, 1, 2, 3, 5, ...
。
递归解法:
- 定义:
fib(n) = fib(n-1) + fib(n-2)
。 - 终止条件:
fib(1) = 1
,fib(2) = 1
。
Java 代码:
java
public class Fibonacci {
public static int fib(int n) {
// 终止条件
if (n == 1 || n == 2) {
return 1;
}
// 自我调用
return fib(n - 1) + fib(n - 2);
}
public static void main(String[] args) {
int result = fib(5);
System.out.println("斐波那契第 5 项 = " + result); // 输出: 5
}
}
七、递归的优缺点
优点 | 缺点 |
---|---|
代码简洁,易理解 | 性能差(重复计算) |
适合树形结构或嵌套问题 | 可能导致栈溢出 |
递归思路自然 | 调试困难 |
八、递归的优化:记忆化搜索
问题:
斐波那契数列递归解法中,fib(3)
被计算了 2 次,造成浪费。
优化:
用数组记录已计算的结果,避免重复计算。
Java 代码:
java
public static int fibOptimized(int n, int[] memo) {
// 直接返回已计算的结果
if (memo[n] != 0) {
return memo[n];
}
// 终止条件
if (n == 1 || n == 2) {
memo[n] = 1;
return 1;
}
// 计算并记录结果
memo[n] = fibOptimized(n - 1, memo) + fibOptimized(n - 2, memo);
return memo[n];
}
// 调用方式
int[] memo = new int[100]; // 存储中间结果
int result = fibOptimized(5, memo);
九、关键总结
-
递归 = 函数自己调用自己,直到满足终止条件。
-
核心:终止条件 + 自我调用(问题规模减小)。
-
适用场景:树形结构、嵌套问题、分治算法。
记忆口诀:
-
递归就像剥洋葱,一层一层往里剥,直到找到核心!
这样解释够简单了吗?如果还有疑问,随时告诉我! 😊