跳转到主内容
思享编程网:思考分享,玩转编程世界!

C语言递归入门

前言 作为C语言初学者,递归绝对是绕不开的一个知识点——它看起来抽象难理解,实则有固定规律,掌握好案例和核心逻辑,就能轻松上手。

今天就结合我自己的学习经历,整理了6个递归经典案例,从简单到复杂,逐句解析、附带可直接运行的代码,还有新手必记的避坑技巧,帮大家快速搞定递归!

先记住递归的3句核心口诀(背下来就能少走90%的弯路): 1.自己调用自己(递归的核心形式) 2.必须有终止条件(否则死递归,栈溢出) 3.大事拆小事(把复杂问题拆解成和原问题相似的小问题) 一、递归基础认知 什么是递归?

简单说就是“函数自己调用自己”,比如求1~n的和,我们可以拆解成“n + 1~n-1的和”,而1~n-1的和又能继续拆解,直到拆解到最小的1(终止条件),再一步步回溯计算结果。

递归的关键的是「终止条件」和「递推逻辑」:没有终止条件会导致程序崩溃,递推逻辑不对则会计算出错,这两个点也是下面所有案例的核心。

二、6个递归经典案例 案例1:递归打印1~n

#include

// 递归打印1~n void print(int n) { if(n == 0) // 终止条件:当n=0时,停止递归(避免死递归) return; print(n - 1); // 递推:先调用自己,n不断减小(先递后打) printf("%d ", n); // 回溯时打印,从1到n依次输出 }

int main() { print(5); // 调用函数,打印1~5 return 0; }

运行结果:1 2 3 4 5 解析:核心是“先递后打”——先不断递归调用print(n-1),直到n=0停止,然后从n=1开始回溯,依次打印每个值,刚好实现1~n的顺序输出。

新手容易把print(n-1)和printf的顺序写反,写反会变成5~1倒序打印哦。

案例2:递归求1~n累加和

#include

// 递归求1~n的累加和 int sum(int n) { if(n == 1) // 终止条件:n=1时,和为1(最小问题) return 1; // 递推逻辑:n的和 = n + (1~n-1的和),自己调用自己求1~n-1的和 return n + sum(n - 1); }

int main() { printf("1~5的和:%d", sum(5)); // 输出:15 return 0; }

逻辑拆解: sum(5) = 5 + sum(4); sum(4) = 4 + sum(3); sum(3) = 3 + sum(2); sum(2) = 2 + sum(1); sum(1) = 1(终止); 回溯计算:2+1=3,3+3=6,6+4=10,10+5=15。

案例3:递归求阶乘

#include

// 递归求n的阶乘 int fac(int n) { if(n == 1 || n == 0) // 终止条件:n=1或n=0时,阶乘为1 return 1; // 递推逻辑:n! = n × (n-1)! return n * fac(n - 1); }

int main() { printf("5的阶乘:%d", fac(5)); // 输出:120 return 0; }

解析:和累加和逻辑类似,都是“大事拆小事”,把n!拆解成n × (n-1)!,直到拆解到1!,再回溯相乘得到结果。

注意:n不能为负数,否则会陷入死递归(可自行添加负数判断优化)。

案例4:递归求斐波那契数列

#include

// 递归求第n项斐波那契数 int fib(int n) { // 终止条件:第1项和第2项都是1 if(n == 1 || n == 2) return 1; // 递推逻辑:第n项 = 第n-1项 + 第n-2项 return fib(n-1) + fib(n-2); }

int main() { printf("斐波那契数列第6项:%d", fib(6)); // 输出:8 return 0; }

注意:递归求斐波那契数列效率不高(会重复计算很多项),但作为递归入门案例非常合适,后期可以学习非递归写法优化。

案例5:递归反转字符串

#include

// 辅助函数:求字符串长度(模拟strlen) int mylen(char *str) { int cnt = 0; while(*str) // 直到遇到'\0'停止计数 { cnt++; str++; } return cnt; }

// 递归反转字符串 void reverse(char *str) { int len = mylen(str); // 获取字符串长度 char temp = *str; // 保存首字符 *str = *(str + len - 1); // 首字符替换为尾字符 *(str + len - 1) = '\0'; // 截断字符串,只留中间部分

// 终止条件:中间部分长度≥2才继续递归 if(mylen(str+1) >= 2) reverse(str + 1); // 递归反转中间部分

*(str + len - 1) = temp; // 回溯,恢复尾字符 }

int main() { char arr[] = "abcde"; // 注意:不能用char *arr = "abcde"(字符串常量不可修改) reverse(arr); printf("反转后:%s", arr); // 输出:edcba return 0; }

解析:这是递归结合指针、数组的经典案例,重点理解“截断字符串”和“回溯恢复”的逻辑,避免反转后字符串缺失字符。

案例6:递归求数组最大值

#include

// 递归求数组最大值 int getMax(int arr[], int n) { if(n == 1) // 终止条件:数组只有1个元素,最大值就是它本身 return arr[0]; // 递推:先求前n-1个元素的最大值,再和第n个元素对比 int m = getMax(arr, n-1); return arr[n-1] > m ? arr[n-1] : m; // 返回较大值 }

int main() { int arr[] = {2, 9, 1, 5, 7}; int max = getMax(arr, 5); // 数组长度为5 printf("数组最大值:%d", max); // 输出:9 return 0; }

解析:把“求n个元素的最大值”拆解成“求n-1个元素的最大值,再和第n个元素对比”,不断缩小数组范围,直到数组只剩1个元素,完成回溯对比。

三、必避的3个递归坑 1.没有终止条件:这是最常见的错误,会导致函数无限调用自己,最终栈溢出(程序崩溃),记住:递归必须有明确的终止条件。

2.递推参数不缩小:比如把print(n-1)写成print(n),参数没有变化,会陷入死递归,递推的核心是“不断缩小问题规模”。

3.混淆递推和回溯的顺序:比如案例1中,把print(n-1)和printf的顺序写反,会导致输出顺序颠倒,要明确“先递后回溯”的逻辑。

相关文章