当前位置: 首页 > news >正文

3.4 递归函数

在这里插入图片描述

1 递归概念

俄罗斯套娃比喻

递归就像打开一个俄罗斯套娃,每个娃娃里面都有一个更小的娃娃,直到最小的那个不能再打开为止。

void openMatryoshka(int size) {if(size == 1) {  // 基本情况 - 最小的娃娃cout << "打开最小的娃娃!" << endl;return;}cout << "打开大小为" << size << "的娃娃" << endl;openMatryoshka(size - 1);  // 打开里面的小娃娃
}

递归三要素

  1. 基本情况(Base Case):递归的终止条件
  2. 递归关系(Recursive Relation):问题与子问题的关系
  3. 向基本情况靠近:每次递归调用都应更接近基本情况

递归与循环的比较

特性递归循环
实现方式函数调用自身重复执行代码块
内存消耗需要栈空间保存每次调用状态只需要固定内存
代码可读性对某些问题更直观通常更直接
适用问题树形结构、分治问题线性重复操作

2 阶乘函数

数学定义

n! = n × (n-1) × … × 1
0! = 1 (特殊情况)

递归实现

int factorial(int n) {// 基本情况if(n == 0 || n == 1) {return 1;}// 递归关系:n! = n * (n-1)!return n * factorial(n - 1);
}

调用过程可视化

factorial(4)
= 4 * factorial(3)
= 4 * (3 * factorial(2))
= 4 * (3 * (2 * factorial(1)))
= 4 * (3 * (2 * 1))
= 4 * (3 * 2)
= 4 * 6
= 24

3 斐波那契数列

数列定义

F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2) (n ≥ 2)

递归解法

int fibonacci(int n) {if(n == 0) return 0;if(n == 1) return 1;return fibonacci(n-1) + fibonacci(n-2);
}

效率问题讨论

递归解法存在重复计算问题。例如计算fib(5):

fib(5)
= fib(4) + fib(3)
= (fib(3) + fib(2)) + (fib(2) + fib(1))
= ... // fib(2)被计算了多次

优化方案:使用记忆化(Memoization)存储已计算结果

int fibMemo(int n, vector<int>& memo) {if(n <= 1) return n;if(memo[n] != -1) return memo[n];memo[n] = fibMemo(n-1, memo) + fibMemo(n-2, memo);return memo[n];
}int fibonacci(int n) {vector<int> memo(n+1, -1);return fibMemo(n, memo);
}

4 递归练习

汉诺塔问题

void hanoi(int n, char from, char to, char aux) {if(n == 1) {cout << "将盘子1从" << from << "移动到" << to << endl;return;}hanoi(n-1, from, aux, to);cout << "将盘子" << n << "从" << from << "移动到" << to << endl;hanoi(n-1, aux, to, from);
}// 调用示例:hanoi(3, 'A', 'C', 'B');

十进制转二进制

void decToBinary(int n) {if(n == 0) return;decToBinary(n / 2);cout << n % 2;
}// 调用示例:decToBinary(10); 输出1010

最大公约数(欧几里得算法)

int gcd(int a, int b) {if(b == 0) return a;return gcd(b, a % b);
}// 调用示例:gcd(48, 18)返回6

递归调试技巧

  1. 添加打印语句:跟踪递归调用
int factorial(int n, int depth = 0) {cout << string(depth, ' ') << "计算factorial(" << n << ")" << endl;if(n == 0) return 1;int result = n * factorial(n-1, depth+1);cout << string(depth, ' ') << "返回" << result << endl;return result;
}
  1. 绘制调用树:在纸上画出递归过程
  2. 使用调试器:观察调用栈的变化

常见递归错误

  1. 缺少基本情况:导致无限递归
// 错误示例!
int factorial(int n) {return n * factorial(n-1); // 没有终止条件!
}
  1. 不向基本情况靠近
// 错误示例!
int badRecursion(int n) {if(n == 0) return 1;return badRecursion(n); // 永远不会结束!
}
  1. 栈溢出:递归太深导致调用栈溢出

递归与迭代转换

任何递归算法都可以转换为迭代形式(使用栈):

// 迭代版阶乘
int factorialIter(int n) {int result = 1;for(int i = 1; i <= n; i++) {result *= i;}return result;
}

本章综合练习

  1. 基础题:实现递归函数计算数字各位之和,如sumDigits(123)=6
  2. 提高题:使用递归判断字符串是否是回文
  3. 挑战题:实现递归的二分查找算法
  4. 思考题:分析递归计算斐波那契数列的时间复杂度

编程项目:实现一个递归的文件系统扫描器,输出指定目录下的所有文件树形结构(伪代码):

void printFileTree(string path, int depth = 0) {// 打印当前目录/文件(带缩进)// 如果是目录://   获取所有子项//   对每个子项递归调用printFileTree
}

性能优化建议

  1. 对于深度递归问题,考虑使用迭代解法
  2. 使用记忆化(Memoization)优化重复计算
  3. 尾递归优化(某些编译器支持)
  4. 限制递归深度(设置最大深度)
http://www.dtcms.com/a/291299.html

相关文章:

  • GUI简介
  • CMake变量和环境变量之间的关系和区别CMAKE_EXPORT_COMPILE_COMMANDS环境变量作用
  • Weex 知识点
  • SymPy 中抽象函数求导与具体函数代入的深度解析
  • C多线程下的fwrite与write:深入挖掘与实战指南
  • 每日算法刷题Day51:7.21:leetcode 栈6道题,用时1h40min
  • 【项目实战】——深度学习.全连接神经网络
  • PostgreSQL SysCache RelCache
  • Java API (二):从 Object 类到正则表达式的核心详解
  • DevOps是什么?
  • Flutter中 Provider 的基础用法超详细讲解(一)
  • C++的“链”珠妙笔:list的编程艺术
  • JAVA序列化知识小结
  • mac终端设置代理
  • 拟合算法(1)
  • socket编程(UDP)
  • QGIS、ArcMap、ArcGIS Pro中的书签功能、场景裁剪
  • 本地部署Dify、Docker重装
  • 时序论文43 | WPMixer:融合小波分解的多分辨率长序列预测模型
  • Nginx配置proxy protocol代理获取真实ip
  • ubuntu远程桌面不好使
  • 修复echarts由4.x升级5.x出现地图报错echarts/map/js/china.js未找到
  • 卷积神经网络基本概念
  • 深度学习之参数初始化和损失函数(四)
  • 深入解析MIPI C-PHY (二)C-PHY三线魔术:如何用6种“符号舞步”榨干每一滴带宽?
  • 设计模式六:工厂模式(Factory Pattern)
  • C语言:20250721笔记
  • 在 Conda 中删除环境及所有安装的库
  • 《使用 IDEA 部署 Docker 应用指南》
  • Linux-rpm和yum