
前言
很多人初次接触递归时,往往感到迷茫甚至恐惧。看到函数调用自己,就不知道代码到底是如何执行的。其实这很正常,本文的目的不仅是讲解两道题,更是要帮大家建立清晰的递归思路,彻底消除这种畏难情绪。
递归的核心思维
看待递归最关键的一点是:明确函数的功能。递归的本质是自己调用自己,就像我们平时调用其他函数一样,是为了利用它的功能。
以归并排序为例,mergesort 的功能就是让一个无序数组变成有序。当我们写 mergesort(nums, left, mid) 时,心里要清楚:此时左边数组已经有序了。至于它具体怎么实现的?不要去纠结细节,相信这个函数能帮我们完成排序任务。同理处理右边数组。两边都排好序后,再合并即可。
这种宏观视角非常重要。不要下意识地去画递归展开图,而是抱着信任的态度:只要满足结束条件,函数就能完成既定目标。当然,必须保证递归有出口,即函数开头要有明确的终止条件。
1. 汉诺塔
题目描述
有三根柱子 A、B、C,A 柱上有 n 个大小不同的圆盘,从小到大叠放。要求将 A 柱上的所有盘子移动到 C 柱,移动过程中大盘子不能压在小盘子上面,且每次只能移动一个盘子。
算法思路
这是一个经典的递归问题。我们可以从简单情况入手:
- n=1:直接将盘子从 A 移到 C。
- n=2:借助 B 柱。先将小盘(1 号)移到 B,大盘(2 号)移到 C,最后将小盘从 B 移到 C。
- n>2:策略同上。将 A 上面的 n-1 个盘子移到 B,将最大的盘子移到 C,最后将 B 上的 n-1 个盘子移到 C。
核心逻辑可以归纳为:
- 将 n-1 个盘子从源柱借助目标柱移到辅助柱。
- 将第 n 个盘子从源柱直接移到目标柱。
- 将 n-1 个盘子从辅助柱借助源柱移到目标柱。
当规模为 1 时,直接移动。
C++ 实现
class Solution {
public:
void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
dfs(A, B, C, A.size());
}
void dfs(vector<int>& x, vector<int>& y, vector<int>& z, int n) {
if (n == ) {
z.(x.());
x.();
;
}
(x, z, y, n - );
z.(x.());
x.();
(y, x, z, n - );
}
};




