// 奇偶数判断的互递归
bool isEven(int n); // 前向声明
bool isOdd(int n) {
if (n == 0) return false;
return isEven(n - 1);
}
bool isEven(int n) {
if (n == 0) return true;
return isOdd(n - 1);
}
// 表达式求值的互递归
int term(); // 前向声明
int factor() {
// 解析因子
if (当前是数字) return 数字值;
if (当前是左括号) {
消耗左括号;
int val = term();
消耗右括号;
return val;
}
}
int term() {
// 解析项
int val = factor();
while (当前是乘除运算符) {
char op = 当前运算符;
消耗运算符;
int right = factor();
val = (op == '*') ? val * right : val / right;
}
return val;
}
基本递归模板
ReturnType recursiveFunction(Parameters params) {
// 1. 基本情况检查
if (baseCase) {
return baseValue;
}
// 2. 处理当前层(可选)
// 进行一些必要的处理
// 3. 递归调用
ReturnType result = recursiveFunction(smallerProblem);
// 4. 处理返回值(可选)
// 根据需要处理递归调用的结果
return finalResult;
}
带记忆化的递归模板
ReturnType memoizedFunction(Parameters params) {
// 1. 检查是否已计算
if (已经计算过) {
return 缓存的结果;
}
// 2. 基本情况检查
if (baseCase) {
return baseValue;
}
// 3. 递归调用
ReturnType result = memoizedFunction(smallerProblem);
// 4. 存储结果
缓存结果;
return result;
}
普通递归转尾递归的模板
ReturnType recursiveFunction(Parameters params) {
return recursiveHelper(params, 初始累积值);
}
ReturnType recursiveHelper(Parameters params, AccumulatorType acc) {
// 1. 基本情况检查
if (baseCase) {
return acc;
}
// 2. 计算新的累积值
AccumulatorType newAcc = 更新累积值;
// 3. 尾递归调用
return recursiveHelper(smallerProblem, newAcc);
}
示例:阶乘计算
int factorial(int n) {
return factorialHelper(n, 1);
}
int factorialHelper(int n, int acc) {
if (n <= 1) return acc;
return factorialHelper(n - 1, n * acc);
}
示例:数组求和
int arraySum(vector<int>& arr) {
return arraySumHelper(arr, arr.size(), 0);
}
int arraySumHelper(vector<int>& arr, int n, int sum) {
if (n <= 0) return sum;
return arraySumHelper(arr, n - 1, sum + arr[n-1]);
}
问题描述:
递归分析:
基本思路
递归过程
graph TD
A[移动n个盘子] --> B[移动n-1个盘子到辅助柱]
B --> C[移动第n个盘子到目标柱]
C --> D[移动n-1个盘子到目标柱]
复杂度分析
关键步骤
/**
* 汉诺塔递归求解函数
* @param n: 要移动的盘子数量
* @param from: 源柱子
* @param aux: 辅助柱子
* @param to: 目标柱子
*/
void hanoi(int n, char from, char aux, char to) {
// 基本情况:只有一个盘子时直接移动
if (n == 1) {
cout << "Move disk 1 from " << from << " to " << to << endl;
return;
}
// 递归步骤1:将n-1个盘子从源柱移到辅助柱
hanoi(n - 1, from, to, aux);
// 步骤2:将第n个盘子从源柱移到目标柱
cout << "Move disk " << n << " from " << from << " to " << to << endl;
// 递归步骤3:将n-1个盘子从辅助柱移到目标柱
hanoi(n - 1, aux, from, to);
}
// 使用示例
int main() {
int n = 3; // 盘子数量
hanoi(n, 'A', 'B', 'C');
return 0;
}
问题描述:
分治策略分析:
基本思路
递归过程
graph TD
A[原始数组] --> B[左半部分]
A --> C[右半部分]
B --> D[左左]
B --> E[左右]
C --> F[右左]
C --> G[右右]
D --> H[合并左半]
E --> H
F --> I[合并右半]
G --> I
H --> J[最终合并]
I --> J
复杂度分析
优点与特性
/**
* 合并两个已排序的子数组
* @param arr: 原数组
* @param left: 左边界
* @param mid: 中间位置
* @param right: 右边界
*/
void merge(vector<int>& arr, int left, int mid, int right) {
vector<int> temp(right - left + 1); // 临时数组
int i = left, j = mid + 1, k = 0;
// 合并两个有序数组
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
// 处理剩余元素
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
// 将临时数组复制回原数组
for (i = 0; i < k; i++) {
arr[left + i] = temp[i];
}
}
/**
* 归并排序主函数
* @param arr: 待排序数组
* @param left: 左边界
* @param right: 右边界
*/
void mergeSort(vector<int>& arr, int left, int right) {
// 基本情况:数组长度为1或0
if (left >= right) return;
// 计算中间位置
int mid = left + (right - left) / 2;
// 递归排序左半部分
mergeSort(arr, left, mid);
// 递归排序右半部分
mergeSort(arr, mid + 1, right);
// 合并已排序的子数组
merge(arr, left, mid, right);
}
// 使用示例
int main() {
vector<int> arr = {64, 34, 25, 12, 22, 11, 90};
mergeSort(arr, 0, arr.size() - 1);
cout << "Sorted array: ";
for(int num : arr) cout << num << " ";
return 0;
}
问题描述:
递归回溯分析:
基本思路
递归树结构
graph TD
A[开始] --> B[选择1]
A --> C[不选1]
B --> D[选择2]
B --> E[不选2]
C --> F[选择2]
C --> G[不选2]
D --> H[...]
E --> I[...]
F --> J[...]
G --> K[...]
优化策略
复杂度分析
/**
* 生成组合的主函数
* @param n: 数字范围上限
* @param k: 要选择的数字个数
* @return: 所有可能的组合
*/
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> result; // 存储所有组合
vector<int> current; // 当前组合
// 调用辅助函数进行递归生成
combineDFS(n, k, 1, current, result);
return result;
}
/**
* 递归生成组合的辅助函数
* @param n: 数字范围上限
* @param k: 要选择的数字个数
* @param start: 当前可选的起始数字
* @param current: 当前已选数字集合
* @param result: 存储所有组合的结果集
*/
void combineDFS(int n, int k, int start,
vector<int>& current,
vector<vector<int>>& result) {
// 基本情况:找到一个合法组合
if (current.size() == k) {
result.push_back(current);
return;
}
// 优化1:剪枝
// 如果剩余数字不够凑成k个,直接返回
if (current.size() + (n - start + 1) < k) return;
// 递归生成组合
for (int i = start; i <= n; i++) {
// 选择当前数字
current.push_back(i);
// 递归生成剩余位置的组合
combineDFS(n, k, i + 1, current, result);
// 回溯:撤销选择
current.pop_back();
}
}
// 使用示例
int main() {
int n = 4, k = 2;
vector<vector<int>> combinations = combine(n, k);
cout << "All combinations of " << k << " numbers from 1 to " << n << ":\n";
for (const auto& comb : combinations) {
cout << "{ ";
for (int num : comb) cout << num << " ";
cout << "}\n";
}
return 0;
}
问题描述:
递归下降分析:
基本原理
语法树结构
graph TD
A[表达式] --> B[项]
A --> C[+ -]
A --> D[项]
B --> E[因子]
B --> F[* /]
B --> G[因子]
E --> H[数字/括号]
G --> I[数字/括号]
优先级处理
实现策略
/**
* 表达式计算器类
* 使用递归下降法解析和计算数学表达式
*/
class ExpressionEvaluator {
private:
string expr; // 存储表达式
int pos = 0; // 当前处理位置
/**
* 跳过空白字符
*/
void skipSpace() {
while (pos < expr.length() && expr[pos] == ' ') pos++;
}
/**
* 解析表达式(处理加减法)
* @return 表达式的计算结果
*/
int parseExpr() {
int left = parseTerm(); // 先获取第一项
while (pos < expr.length()) {
skipSpace();
if (pos >= expr.length()) break;
char op = expr[pos];
if (op != '+' && op != '-') break;
pos++; // 跳过运算符
int right = parseTerm();
// 执行加减运算
if (op == '+') left += right;
else left -= right;
}
return left;
}
/**
* 解析项(处理乘除法)
* @return 项的计算结果
*/
int parseTerm() {
int left = parseFactor(); // 先获取第一个因子
while (pos < expr.length()) {
skipSpace();
if (pos >= expr.length()) break;
char op = expr[pos];
if (op != '*' && op != '/') break;
pos++; // 跳过运算符
int right = parseFactor();
// 执行乘除运算
if (op == '*') left *= right;
else {
if (right == 0) throw runtime_error("Division by zero");
left /= right;
}
}
return left;
}
/**
* 解析因子(处理数字和括号)
* @return 因子的值
*/
int parseFactor() {
skipSpace();
// 处理括号表达式
if (expr[pos] == '(') {
pos++; // 跳过左括号
int val = parseExpr();
skipSpace();
if (pos >= expr.length() || expr[pos] != ')')
throw runtime_error("Missing closing parenthesis");
pos++; // 跳过右括号
return val;
}
// 处理数字
if (!isdigit(expr[pos]))
throw runtime_error("Invalid character: " + string(1, expr[pos]));
int val = 0;
while (pos < expr.length() && isdigit(expr[pos])) {
val = val * 10 + (expr[pos] - '0');
pos++;
}
return val;
}
public:
/**
* 构造函数
* @param s 要计算的表达式
*/
ExpressionEvaluator(string s) : expr(s) {}
/**
* 计算表达式的值
* @return 表达式的计算结果
*/
int evaluate() {
pos = 0; // 重置位置
int result = parseExpr();
skipSpace();
if (pos < expr.length())
throw runtime_error("Invalid expression");
return result;
}
};
// 使用示例
int main() {
string expr = "2 * (3 + 4) * 5";
ExpressionEvaluator eval(expr);
try {
cout << "Expression: " << expr << endl;
cout << "Result: " << eval.evaluate() << endl;
} catch (const exception& e) {
cout << "Error: " << e.what() << endl;
}
return 0;
}
[POJ 1664] 放苹果
[HDU 2064] 汉诺塔III
[NOIP 2002] 分解因数
[POJ 2506] 超级楼梯
[NOIP 2009] 分形之城
[CSP-J 2019] 次数查询
[NOIP 2014] 生成树计数
[POJ 1192] 最优矩阵链乘
[USACO 2015] 递归函数
[IOI 2000] 邮票
[NOIP 2016] 组合数问题
[POJ 2115] C Looooops

演讲者备注不会在演示模式中显示给观众,仅供演讲者参考。 每个关键页面都添加了备注,帮助演讲者更好地讲解内容。
封面页: - 欢迎学生,简要介绍本次课程的主题和目标 - 强调递归是算法设计中的重要思想 - 本讲义将从基本概念到实际应用,系统讲解递归的思想和技巧
目录页: - 简要介绍讲义结构 - 递归基本概念、类型、设计步骤、模板代码、优化技巧、经典例题
转场页: - 引入递归算法的基本概念 - 递归是算法设计的基础思想之一
递归概述: - 递归的定义和核心思想 - 递归与迭代的区别
转场页: - 递归的常见类型
线性递归: - 定义和特点 - 常见的线性递归例子 - 线性递归的实现方式
树形递归: - 定义和特点 - 常见的树形递归例子 - 树形递归的实现方式
尾递归: - 定义和特点 - 常见的尾递归例子 - 尾递归的实现方式
互递归: - 定义和特点 - 常见的互递归例子 - 互递归的实现方式
转场页: - 递归算法的设计流程
转场页: - 递归算法的代码模板
转场页: - 递归算法的优化方法
转场页: - 递归经典例题
转场页: - 总结递归算法的核心思想和应用
转场页: - 递归算法练习题目 - 从各大OJ平台和竞赛中精选的递归题目
练习题目部分结束,这些题目涵盖了递归算法的各个难度和应用场景。 鼓励学生尝试解决这些问题,加深对递归思想的理解。