L5 - 函数与递归
L5 - 函数与递归
🎯 教学目标深化
- 函数设计原则
- 单一职责原则(每个函数只做一件事)
- 合理控制函数体长度(建议不超过50行)
- 避免函数副作用(除非明确需要)
- 递归思维训练
- 分治思想的实现方式
- 递归树分析(用于理解递归执行路径)
- 递归与数学归纳法的关系
🔑 核心知识点扩展
1. 函数定义深度解析
函数声明与定义的分离
1 | // mymath.h(头文件) |
头文件作用:
- 提供接口文档
- 避免重复定义
- 支持模块化编译
默认参数机制
1 | void printLog(string msg, int level=1) { |
规则说明:
- 默认参数必须从右向左连续设置
- 默认值在声明中指定(定义中不再重复)
- 默认参数慎用指针和引用类型
2. 参数传递机制对比(新增指针传递)
传递方式 | 语法 | 内存操作 | 典型应用场景 |
---|---|---|---|
值传递 | int a |
创建参数副本 | 基础类型小数据 |
引用传递 | int& a |
直接操作原变量 | 需要修改实参 |
指针传递 | int* a |
操作地址指向数据 | 可选参数/动态结构 |
指针传递示例:
1 | void modifyByPointer(int* ptr, int newVal) { |
3. 递归算法高级应用
递归调用栈可视化
阶乘递归的栈空间变化:
1 | | factorial(3) | → 分配n=3 |
尾递归优化原理
1 | // 普通递归 |
编译器优化效果:
- 将尾递归转换为循环结构
- 避免栈溢出风险
- 时间复杂度从O(n)降为O(1)(空间复杂度)
💡 进阶技巧
1. 函数指针应用
1 | // 定义函数指针类型 |
2. 函数重载规则
1 | void print(int num) { /* 处理整型 */ } |
重载解析规则:
- 精确匹配
- 类型提升(char→int)
- 标准转换(int→double)
- 用户定义转换
🧩 经典算法深度实现
1. 快速排序递归实现
1 | void quickSort(int arr[], int left, int right) { |
执行过程图示:
1 | 原始数组:[5][3][8][6][4][7][1][2] |
2. 二叉树遍历(先序递归)
1 | struct TreeNode { |
递归树分析:
1 | 1 |
💣 易错点深度剖析
1. 栈溢出问题
错误案例:
1 | int fibonacci(int n) { |
解决方案:
- 改用迭代法
- 记忆化缓存(存储已计算结果)
1 | unordered_map<int, int> cache; |
2. 引用传递陷阱
未预期的修改:
1 | void initArray(int& arr) { // 错误!数组引用语法错误 |
🛠️ 高级调试技巧
1. 递归调用追踪
1 | int depth = 0; // 全局变量记录递归深度 |
2. 内存泄漏检测
1 | void testFunction() { |
🏋️ 配套练习升级
1. 函数性能对比器
要求:
- 实现时间测量装饰器函数
- 对比递归与迭代版的斐波那契计算效率
- 输出不同规模下的耗时曲线
参考代码框架:
1 | template<typename Func> |
2. 表达式解析器
要求:
- 实现递归下降解析器
- 支持四则运算和括号
- 示例输入:“3*(2+4)/2”
- 示例输出:9
评论