鹏哥c语言复习第九讲----函数递归
一、递归的核心定义递归是 C 语言中解决问题的重要方法本质是函数自己调用自己核心思想是将大型复杂问题层层拆解为与原问题相似但规模更小的子问题直到子问题无法再拆分递归终止再通过 “回归” 得到原问题的解。简单来说递归的过程分为递推拆分子问题和回归合并子问题结果两个阶段。二、递归的两个必要限制条件递归编写必须满足以下两个条件否则会陷入死递归最终导致栈溢出Stack overflow函数调用的栈帧空间被持续占用直至耗尽存在递归终止条件满足该条件时递归不再继续每次递归调用后问题规模越来越接近终止条件。反例死递归main 函数无限制自调用无终止条件c运行#include stdio.h int main() { printf(hehe\n); main(); // 死递归栈溢出 return 0; }三、递归经典实例解析1. 求 n 的阶乘核心公式n!n∗(n−1)!终止条件0!1递归实现代码c运行#include stdio.h int Fact(int n) { if(n 0) // 终止条件 return 1; else return n * Fact(n-1); // 规模缩小接近终止条件 } int main() { int n 0; scanf(%d, n); int ret Fact(n); printf(%d\n, ret); return 0; }执行过程递推Fact(5)→5∗Fact(4)→5∗4∗Fact(3)→...→5∗4∗3∗2∗1∗Fact(0)回归Fact(0)1→1∗2→1∗2∗3→...→1202. 顺序打印整数的每一位解题思路终止条件数字为一位数时直接打印递推逻辑先打印数字去掉最后一位的部分再打印最后一位通过n/10缩小规模n%10取最后一位。递归实现代码c运行#include stdio.h void Print(int n) { if(n 9) // 终止条件n为一位数时不进入递归 Print(n/10); printf(%d , n%10); } int main() { int m 0; scanf(%d, m); Print(m); return 0; }执行过程以 1234 为例递推Print(1234)→Print(123)→Print(12)→Print(1)回归打印 1 → 打印 2 → 打印 3 → 打印 4四、递归与迭代的对比1. 递归的优缺点优点代码简洁逻辑清晰适合解决复杂问题如汉诺塔、青蛙跳台阶缺点每次递归调用都会在栈区开辟新的函数栈帧占用内存递归层次过深易栈溢出部分场景会产生大量冗余计算效率低下。2. 迭代循环的优缺点优点无函数调用开销内存占用少效率高无栈溢出风险缺点部分复杂问题的迭代逻辑编写难度大代码可读性不如递归。3. 经典场景对比求第 n 个斐波那契数斐波那契数公式Fib(n)1n≤2Fib(n)Fib(n−1)Fib(n−2)n2递归实现低效不推荐问题大量冗余计算如计算 Fib (40) 时Fib (3) 会被计算 39088169 次n 越大计算速度越慢。c运行int Fib(int n) { if(n 2) return 1; else return Fib(n-1) Fib(n-2); }迭代实现高效推荐思路从前往后计算利用变量保存前两个值避免冗余计算。c运行int Fib(int n) { int a1, b1, c1; while(n 2) { c a b; a b; b c; n--; } return c; }4. 递归与迭代的选择原则简单问题如 n 的阶乘迭代效率更高优先选择复杂问题如汉诺塔、青蛙跳台阶递归逻辑更简洁可选择递归避免在数据规模大、存在大量重复计算的场景使用递归如斐波那契数。五、新手学习递归的常见错误1. 遗漏递归终止条件表现程序陷入死递归最终报栈溢出错误原因忘记编写递归的终止逻辑或终止条件写在错误位置避坑写递归代码时先写终止条件再写递推逻辑。2. 递归调用未缩小问题规模表现死递归栈溢出原因每次递归调用的参数与原参数无变化或规模反而变大无法接近终止条件例子求阶乘时写为return n * Fact(n)参数未缩小避坑确保递归调用的参数能逐步逼近终止条件如 n→n-1、n→n/10。3. 过度迷恋递归忽略效率问题表现在斐波那契数、大规模阶乘等场景强行使用递归程序运行极慢或栈溢出避坑牢记递归有函数调用开销和冗余计算风险数据规模大或有重复计算的场景优先用迭代。4. 混淆递归的递推和回归顺序表现结果顺序错误如打印整数每一位时先打印最后一位再递归例子打印 1234 时先写printf(%d , n%10)再写Print(n/10)输出 4 3 2 1避坑理清问题的解决顺序需要先处理子问题的先写递归调用再写当前逻辑。5. 未考虑栈溢出的风险表现递归层次过深如求 10000 的阶乘用递归程序报栈溢出错误原因C 语言栈区空间有限每次递归都会开辟栈帧层数过多会耗尽栈空间避坑递归层次不宜过深超过 1000 层的递归场景优先用迭代。6. 错误处理递归的返回值表现递归函数返回值错误或无返回值需要返回值的场景写 void例子求阶乘的函数写为 void 类型无法返回计算结果避坑根据需求定义递归函数的返回值类型确保每个分支都有对应的 return 语句。六、拓展递归经典问题递归的核心思想适合解决有重复子问题、可层层拆解的问题除了上述实例还有两个经典问题推荐学习青蛙跳台阶问题一只青蛙一次可以跳 1 级或 2 级台阶求跳上 n 级台阶的总方法数汉诺塔问题将 n 个盘子从 A 柱移到 C 柱中间借助 B 柱每次只能移一个盘子且大盘不能压小盘。总结递归的核心是大事化小编写的关键是抓住终止条件和递推逻辑新手不要为了用递归而用递归需结合场景选择递归或迭代牢记效率和栈溢出的问题。