C++ 进阶 — 第 3 课
Student,再用比较函数给它排序。一句话:函数调用自己,每次把问题缩小一点,直到最简单的情况直接返回。
// 循环版:求 1+2+...+n
int sum(int n) {
int s = 0;
for (int i = 1; i <= n; i++) s += i;
return s;
}
// 递归版
int sum(int n) {
if (n == 1) return 1; // 终止条件(最简单的情况)
return n + sum(n - 1); // 缩小问题:n + 前面 n-1 个的和
}
sum(3) = 3 + sum(2)
= 2 + sum(1)
= 1 ← 触底,开始返回
= 2 + 1 = 3
= 3 + 3 = 6
一路拆下去,触底后再一路算回来 —— 像走楼梯下楼再上楼。
| 要素 | 例子 | 没写会怎样 |
|---|---|---|
| 终止条件 | if (n == 1) return 1; | 无限递归 → 栈溢出 💀 |
| 缩小规模 | return n + sum(n - 1); | 不缩小也会无限递归 |
int fact(int n) {
if (n == 0) return 1; // 0! = 1
return n * fact(n - 1); // n! = n * (n-1)!
}
int fib(int n) {
if (n <= 2) return 1; // fib(1)=1, fib(2)=1
return fib(n - 1) + fib(n - 2); // fib(n) = fib(n-1) + fib(n-2)
}
fib(40) 会算到天荒地老 —— 大量重复计算。实际用循环版(你之前写的那个)或加“记忆化”。
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
// gcd(48, 18) = gcd(18, 12) = gcd(12, 6) = gcd(6, 0) = 6
| 对比项 | 递归 | 循环 |
|---|---|---|
| 代码量 | 少、优雅 | 稍多 |
| 可读性 | 贴近数学定义 | 贴近执行过程 |
| 性能 | 函数调用有开销 | 更快 |
| 适合 | 树、图、分治 | 简单重复 |
int sum(int n) {
if (n == 1) return 1;
return n + sum(n - 1);
}
// sum(100000) → 10 万个函数调用堆在栈上 → 栈溢出崩溃
每个函数调用占用一小块内存(栈帧),递归太深会爆。一般递归深度别超过几千到几万。
sum(n) 里又调 sum(n),效果和没写终止条件一样。fact(0) = 1,写成 if (n == 1) 会让 fact(0) 一路递归到负数。fib(40) 会算到天荒地老,因为同一个子问题被反复重算。sum(100000) 就是 10 万个栈帧,深度一般别超过几千到几万。fact(0) 返回什么?(fact 是阶乘函数)int fact(int n),求 n 的阶乘。读入 n,输出 n!。fact(n) = n * fact(n-1),fact(0) = 1。
gcd(a, b) = gcd(b, a % b),终止条件 b == 0。
n == 0)?cout 出来,一层层看它在哪一步开始不对。stack 容器;顺便学队列和优先队列,为后面的 BFS、贪心打基础。