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;
}

十、总结 #

递归要点 #

要点 说明
基准情况 必须有终止条件
递归调用 问题规模必须减小
栈空间 注意栈溢出
优化 尾递归、记忆化

最佳实践 #

  1. 确保有基准情况
  2. 确保问题规模减小
  3. 考虑使用记忆化
  4. 注意栈溢出风险
  5. 必要时转换为迭代

下一步,让我们学习Lambda表达式!

最后更新:2026-03-26