基础
递归算法
递归是信息学竞赛中最重要的基础算法思想之一,也是许多高级算法的基础。本文将系统讲解递归的原理、实现方法和应用技巧,帮助竞赛选手全面掌握这一关键算法。
递归的基本概念
1. 什么是递归
递归是指函数直接或间接调用自身的过程。它包含两个关键要素:
- 递归边界:确定递归何时结束
- 递归式:将原问题分解为更小的同类问题
2. 递归三要素
- 明确函数功能:确定函数要解决什么问题
- 寻找递归边界:确定递归终止条件
- 找出递推关系:如何从小问题的解得到大问题的解
递归基础实现
1. 阶乘函数
题面:
给定一个非负整数n(0≤n≤12),计算n的阶乘。阶乘定义为:n! = n × (n-1) × ... × 1,且0! = 1。
输入格式:一个整数n
输出格式:n!的值
样例输入:
5
样例输出:
120
2. 斐波那契数列
题面:
计算斐波那契数列第n项的值。斐波那契数列定义:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)(n≥2)。
输入格式:整数n(0≤n≤20)
输出格式:F(n)的值
样例输入:
6
样例输出:
8
3. 汉诺塔问题
汉诺塔(Tower of Hanoi)是一个源于印度古老传说的益智玩具。有三根柱子(通常标记为 A、B、C),在柱子 A 上从下往上按照大小顺序摞着若干个圆盘。要求把圆盘从柱子 A 借助柱子 B 移动到柱子 C,并且在移动过程中要满足以下规则:
- 每次只能移动一个圆盘。
- 任何时候都不能把较大的圆盘放在较小的圆盘上面。

递归算法思路
递归是一种编程技巧,在函数的定义中使用函数自身的方法。对于汉诺塔问题,我们可以将其分解为更小的子问题。
假设要将 n 个圆盘从柱子 A 移动到柱子 C,借助柱子 B,我们可以按照以下步骤进行:
- 第一步:将上面的 n - 1 个圆盘从柱子 A 借助柱子 C 移动到柱子 B。
- 第二步:将最大的圆盘(第 n 个圆盘)从柱子 A 移动到柱子 C。
- 第三步:将 n - 1 个圆盘从柱子 B 借助柱子 A 移动到柱子 C。
代码实现
下面是用 C++ 实现汉诺塔问题的代码:
代码解释
-
函数
hanoi
:- 该函数接收四个参数:
n
表示圆盘的数量,source
表示源柱子,auxiliary
表示辅助柱子,destination
表示目标柱子。 - 如果
n
等于 1,直接将圆盘从源柱子移动到目标柱子,并输出移动信息。 - 否则,先递归调用
hanoi
函数将 n - 1 个圆盘从源柱子借助目标柱子移动到辅助柱子。 - 然后将最大的圆盘从源柱子移动到目标柱子,并输出移动信息。
- 最后递归调用
hanoi
函数将 n - 1 个圆盘从辅助柱子借助源柱子移动到目标柱子。
- 该函数接收四个参数:
-
main
函数:- 定义圆盘的数量为 3。
- 调用
hanoi
函数,将 3 个圆盘从柱子 A 借助柱子 B 移动到柱子 C。
图文示例
假设我们有 3 个圆盘,初始状态如下:
- 第一步:将上面的 2 个圆盘从柱子 A 借助柱子 C 移动到柱子 B。
- 先将 1 个圆盘从 A 移动到 C。
- 再将 1 个圆盘从 A 移动到 B。
- 最后将 1 个圆盘从 C 移动到 B。
- 第二步:将最大的圆盘(第 3 个圆盘)从柱子 A 移动到柱子 C。
- 第三步:将 2 个圆盘从柱子 B 借助柱子 A 移动到柱子 C。
- 先将 1 个圆盘从 B 移动到 A。
- 再将 1 个圆盘从 B 移动到 C。
- 最后将 1 个圆盘从 A 移动到 C。
通过这种方式,我们就完成了 3 个圆盘的汉诺塔移动。对于更多圆盘的情况,递归算法也能同样适用。
一些技巧
- 参数设计:尽可能减少递归函数的参数数量
- 全局变量:合理使用全局变量减少参数传递
- 递归深度:注意系统栈限制,避免栈溢出
- 状态恢复:回溯时要完全恢复原始状态
注意事项
- 栈溢出风险:递归深度过大时可能栈溢出
- 重复计算问题:注意使用记忆化优化
- 参数传递效率:尽量使用引用减少拷贝
- 全局变量使用:合理使用全局变量简化代码
- 边界条件检查:确保递归能够正确终止
递归是信息学竞赛的基础也是难点,需要通过大量练习来掌握。建议从简单题目入手,逐步提高难度,同时注意分析每道题的时间复杂度,避免因递归效率问题导致超时。
例题
Status
Problem
Tags