C++递归函数 #
一、递归概述 #
递归是函数直接或间接调用自身的过程。
1.1 递归的组成 #
- 基准情况:递归终止条件
- 递归调用:问题分解为更小的子问题
1.2 基本结构 #
cpp
returnType recursion(parameters) {
if (baseCase) {
// 基准情况:直接返回结果
return baseValue;
}
// 递归调用
return recursion(smallerProblem);
}
二、经典递归示例 #
2.1 阶乘 #
cpp
// n! = n * (n-1)!
// 0! = 1
int factorial(int n) {
if (n <= 1) { // 基准情况
return 1;
}
return n * factorial(n - 1); // 递归调用
}
int main() {
std::cout << factorial(5) << std::endl; // 120
return 0;
}
2.2 斐波那契数列 #
cpp
// F(n) = F(n-1) + F(n-2)
// F(0) = 0, F(1) = 1
int fibonacci(int n) {
if (n <= 0) return 0; // 基准情况
if (n == 1) return 1; // 基准情况
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main() {
for (int i = 0; i < 10; i++) {
std::cout << fibonacci(i) << " ";
}
// 0 1 1 2 3 5 8 13 21 34
return 0;
}
2.3 幂运算 #
cpp
// x^n = x * x^(n-1)
// x^0 = 1
double power(double x, int n) {
if (n == 0) return 1;
if (n < 0) return 1.0 / power(x, -n);
return x * power(x, n - 1);
}
// 优化:快速幂
double fastPower(double x, int n) {
if (n == 0) return 1;
if (n < 0) return 1.0 / fastPower(x, -n);
double half = fastPower(x, n / 2);
if (n % 2 == 0) {
return half * half;
} else {
return x * half * half;
}
}
2.4 最大公约数 #
cpp
// 欧几里得算法
// gcd(a, b) = gcd(b, a % b)
// gcd(a, 0) = a
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
int main() {
std::cout << gcd(48, 18) << std::endl; // 6
return 0;
}
三、递归与数组 #
3.1 数组求和 #
cpp
int arraySum(int arr[], int size) {
if (size <= 0) return 0;
return arr[size - 1] + arraySum(arr, size - 1);
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
std::cout << arraySum(arr, 5) << std::endl; // 15
return 0;
}
3.2 数组最大值 #
cpp
int arrayMax(int arr[], int size) {
if (size == 1) return arr[0];
int maxOfRest = arrayMax(arr, size - 1);
return (arr[size - 1] > maxOfRest) ? arr[size - 1] : maxOfRest;
}
3.3 二分查找 #
cpp
int binarySearch(int arr[], int left, int right, int target) {
if (left > right) return -1; // 未找到
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] > target) {
return binarySearch(arr, left, mid - 1, target);
} else {
return binarySearch(arr, mid + 1, right, target);
}
}
3.4 数组反转 #
cpp
void reverseArray(int arr[], int left, int right) {
if (left >= right) return;
std::swap(arr[left], arr[right]);
reverseArray(arr, left + 1, right - 1);
}
四、递归与字符串 #
4.1 字符串长度 #
cpp
int strLength(const char* str) {
if (*str == '\0') return 0;
return 1 + strLength(str + 1);
}
4.2 字符串反转 #
cpp
void reverseString(char* str, int left, int right) {
if (left >= right) return;
std::swap(str[left], str[right]);
reverseString(str, left + 1, right - 1);
}
4.3 判断回文 #
cpp
bool isPalindrome(const char* str, int left, int right) {
if (left >= right) return true;
if (str[left] != str[right]) return false;
return isPalindrome(str, left + 1, right - 1);
}
五、递归与树 #
5.1 二叉树节点 #
cpp
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
5.2 树的高度 #
cpp
int treeHeight(TreeNode* root) {
if (root == nullptr) return 0;
return 1 + std::max(treeHeight(root->left), treeHeight(root->right));
}
5.3 节点数量 #
cpp
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
5.4 前序遍历 #
cpp
void preorderTraversal(TreeNode* root) {
if (root == nullptr) return;
std::cout << root->val << " "; // 访问根
preorderTraversal(root->left); // 遍历左子树
preorderTraversal(root->right); // 遍历右子树
}
5.5 中序遍历 #
cpp
void inorderTraversal(TreeNode* root) {
if (root == nullptr) return;
inorderTraversal(root->left); // 遍历左子树
std::cout << root->val << " "; // 访问根
inorderTraversal(root->right); // 遍历右子树
}
六、递归优化 #
6.1 尾递归 #
尾递归是递归调用作为函数最后操作的递归。
cpp
// 非尾递归
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // 乘法在递归之后
}
// 尾递归
int factorialTail(int n, int accumulator = 1) {
if (n <= 1) return accumulator;
return factorialTail(n - 1, n * accumulator); // 递归是最后操作
}
6.2 记忆化 #
cpp
#include <unordered_map>
std::unordered_map<int, long long> memo;
long long fibonacciMemo(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
if (memo.find(n) != memo.end()) {
return memo[n];
}
memo[n] = fibonacciMemo(n - 1) + fibonacciMemo(n - 2);
return memo[n];
}
6.3 动态规划 #
cpp
// 斐波那契的动态规划实现
long long fibonacciDP(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
long long prev2 = 0, prev1 = 1;
for (int i = 2; i <= n; i++) {
long long curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
七、递归 vs 迭代 #
7.1 对比 #
| 特性 | 递归 | 迭代 |
|---|---|---|
| 代码简洁性 | 通常更简洁 | 可能更复杂 |
| 空间效率 | 使用调用栈 | 通常更高效 |
| 栈溢出风险 | 有 | 无 |
| 调试难度 | 较难 | 较易 |
7.2 示例对比 #
cpp
// 递归版本
int sumRecursive(int n) {
if (n <= 0) return 0;
return n + sumRecursive(n - 1);
}
// 迭代版本
int sumIterative(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
7.3 选择建议 #
- 使用递归:树遍历、分治算法、问题自然递归定义
- 使用迭代:简单循环、性能关键、深度可能很大
八、递归陷阱 #
8.1 栈溢出 #
cpp
// 危险:深度递归可能导致栈溢出
void deepRecursion(int n) {
if (n <= 0) return;
deepRecursion(n - 1); // n很大时可能栈溢出
}
// 解决:使用迭代或尾递归优化
8.2 重复计算 #
cpp
// 朴素斐波那契有大量重复计算
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2); // 重复计算
}
// 解决:使用记忆化
8.3 无限递归 #
cpp
// 错误:缺少基准情况
int badRecursion(int n) {
return badRecursion(n - 1); // 无限递归
}
// 正确:添加基准情况
int goodRecursion(int n) {
if (n <= 0) return 0; // 基准情况
return n + goodRecursion(n - 1);
}
九、最佳实践 #
9.1 确保基准情况 #
cpp
int recursion(int n) {
// 首先检查基准情况
if (n <= 0) return 0;
// 然后进行递归
return n + recursion(n - 1);
}
9.2 确保问题规模减小 #
cpp
// 错误:问题规模没有减小
int bad(int n) {
return bad(n); // 无限递归
}
// 正确:问题规模减小
int good(int n) {
if (n <= 0) return 0;
return n + good(n - 1);
}
9.3 考虑使用记忆化 #
cpp
std::unordered_map<int, long long> cache;
long long compute(int n) {
if (cache.find(n) != cache.end()) {
return cache[n];
}
long long result = /* 计算 */;
cache[n] = result;
return result;
}
十、总结 #
递归要点 #
| 要点 | 说明 |
|---|---|
| 基准情况 | 必须有终止条件 |
| 递归调用 | 问题规模必须减小 |
| 栈空间 | 注意栈溢出 |
| 优化 | 尾递归、记忆化 |
最佳实践 #
- 确保有基准情况
- 确保问题规模减小
- 考虑使用记忆化
- 注意栈溢出风险
- 必要时转换为迭代
下一步,让我们学习Lambda表达式!
最后更新:2026-03-26