欢迎光临
我们一直在努力

函数递归从入门

函数递归从入门到实战,零基础彻底吃透

在这里插入图片描述

很多初学C语言的同学,提到递归就头疼:看不懂调用过程、写不出递归代码、总遇到栈溢出、死递归问题。但实际上,递归是C语言中最巧妙、最经典的编程思想之一,核心逻辑只有八个字:大事化小,小事化了。 今天这篇博客,我将带你从零入门递归,吃透递归核心原理、执行流程、书写规范,搭配入门案例+高频实战真题,同时总结递归避坑指南、优劣对比,看完就能独立写出规范的递归代码,彻底告别递归盲区。

目录

  • 一、什么是递归?通俗理解
    • 1. 递归的定义
    • 2. 递归两大核心必备条件(重中之重)
    • 3. 递归的两个执行阶段
  • 二、递归入门:最简代码演示与错误示范
    • 1. 错误示范:无终止条件(死递归)
    • 2. 正确入门案例:打印 1~n
  • 三、基础实战:新手必练3个经典递归案例
    • 案例1:递归计算 n的阶乘
    • 案例2:递归计算 1~n累加和
    • 案例3:递归求整数各位数字之和
  • 四、进阶实战:面试高频递归真题
    • 实战1:斐波那契数列
    • 实战2:递归实现数组元素求和(数组核心应用)
    • 实战3:青蛙跳台阶问题(选择+递归经典面试题)
  • 五、递归核心避坑指南(新手必看)
  • 六、递归通用解题模板(直接套用)
  • 七、总结
  • 课后练习

一、什么是递归?通俗理解

1. 递归的定义

递归:函数在执行过程中,直接或间接调用自身的编程方式。 通俗来说,就是把一个复杂的大问题,拆解成和原问题逻辑一致、但规模更小的子问题,不断拆分,直到问题简单到可以直接得出答案,再逐层回溯,最终解决原问题。 生活经典例子:查字典 你不认识A字,查字典发现A的解释里有B字不认识;继续查B字,发现B的解释有C字不认识……直到查到一个完全认识的字(终止条件),再回头依次理解C、B、A的含义,这就是完整的递归过程。

2. 递归两大核心必备条件(重中之重)

任何合法、可运行的递归函数,必须同时满足两个条件,缺一不可,这是杜绝死递归、栈溢出的关键:

  • 递归出口(终止条件):递归的「刹车」,当满足该条件时,停止自我调用,直接返回结果,避免无限递归。
  • 递归递推:函数调用自身,且每次调用必须缩小问题规模,不断向终止条件靠拢。

3. 递归的两个执行阶段

递归的执行全程分为两步,看懂这两步就看懂了所有递归代码:

  • 递推阶段:函数不断调用自身,问题规模持续缩小,直到触发终止条件,停止递推。
  • 回溯阶段:从终止条件的最简结果开始,逐层向上返回,汇总每一层的计算结果,最终得到原问题答案。
  • 二、递归入门:最简代码演示与错误示范

    1. 错误示范:无终止条件(死递归)

    很多新手写递归的第一个坑:忘记写终止条件,程序无限调用自身,最终触发栈溢出(Stack Overflow)。

    #include <stdio.h>
    // 错误代码:无终止条件,死递归
    void test()
    {
    printf("递归执行中\\n");
    test(); // 无限自我调用
    }

    int main()
    {
    test();
    return 0;
    }

    报错原因:C语言中函数调用会开辟栈帧,无限递归会耗尽系统栈空间,程序直接崩溃。

    2. 正确入门案例:打印 1~n

    需求:用递归实现输入n,正序打印 1 到 n 的所有整数。 思路拆解:

    • 终止条件:n <= 0 时,停止递归
    • 递推逻辑:先递归打印 1~n-1,再打印n(先递推后输出,实现正序)

    #include <stdio.h>

    // 递归打印1~n
    void printNum(int n)
    {
    // 递归出口
    if (n <= 0)
    {
    return;
    }
    printNum(n 1); // 递推:缩小问题规模
    printf("%d ", n); // 回溯输出
    }

    int main()
    {
    int n = 5;
    printNum(n);
    return 0;
    }

    执行流程拆解(n=5): 递推:printNum(5)→printNum(4)→printNum(3)→printNum(2)→printNum(1)→printNum(0)(触发终止) 回溯:逐层返回,依次打印 1 2 3 4 5

    三、基础实战:新手必练3个经典递归案例

    掌握基础逻辑后,我们通过3个高频基础案例,彻底熟练递归的书写思路与执行逻辑。

    案例1:递归计算 n的阶乘

    数学公式:n! = 123*…*n,规定 0! = 1,1! = 1 递归思路:n! = n * (n-1)!,把n的阶乘拆解为「n × n-1的阶乘」

    • 终止条件:n == 1 || n == 0,返回1
    • 递推逻辑:return n * factorial(n-1)

    #include <stdio.h>

    int factorial(int n)
    {
    // 递归出口
    if (n == 0 || n == 1)
    {
    return 1;
    }
    return n * factorial(n 1); // 递归拆解
    }

    int main()
    {
    int n = 5;
    printf("%d! = %d\\n", n, factorial(n)); // 输出 120
    return 0;
    }

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

    需求:计算 1+2+3+…+n 的结果 递归思路:sum(n) = sum(n-1) + n

    • 终止条件:n == 1,返回1
    • 递推逻辑:每次累加当前n,再叠加n-1的累加和

    #include <stdio.h>

    int getSum(int n)
    {
    if (n == 1)
    {
    return 1;
    }
    return getSum(n 1) + n;
    }

    int main()
    {
    printf("1~10累加和:%d\\n", getSum(10)); // 输出55
    return 0;
    }

    案例3:递归求整数各位数字之和

    需求:不使用指针、字符串相关知识,纯通过变量、算术运算+递归,计算一个整数的所有各位数字之和,适配新手知识点范围。 递归思路:

    • 终止条件:整数小于10时,数字本身就是最后一位,直接返回自身
    • 递推逻辑:通过取余运算拿到最后一位数字,再整除10去掉最后一位,递归计算剩余数字的和,最终累加结果

    #include <stdio.h>

    // 递归计算整数各位数字之和
    int getDigitSum(int num)
    {
    // 递归出口:个位数直接返回本身
    if (num < 10)
    {
    return num;
    }
    // 取余得到最后一位数字,递归累加剩余数字和
    return num % 10 + getDigitSum(num / 10);
    }

    int main()
    {
    int num = 12345;
    printf("数字%d的各位之和:%d\\n", num, getDigitSum(num)); // 输出15
    return 0;
    }

    四、进阶实战:面试高频递归真题

    掌握基础案例后,我们攻克3个面试、考试高频递归真题,吃透递归核心应用场景。

    实战1:斐波那契数列

    需求:求第n项斐波那契数,数列规则:1,1,2,3,5,8… 前两项为1,从第三项开始,每一项=前两项之和 递归公式:fib(n) = fib(n-1) + fib(n-2)

    • 终止条件:n1 || n2,返回1

    #include <stdio.h>

    int fib(int n)
    {
    if (n == 1 || n == 2)
    {
    return 1;
    }
    return fib(n 1) + fib(n 2);
    }

    int main()
    {
    // 打印前10项斐波那契数列
    for (int i = 1; i <= 10; i++)
    {
    printf("%d ", fib(i));
    }
    return 0;
    }

    重点优化提醒:递归实现斐波那契存在大量重复计算,n较大时效率极低,实际开发中优先用循环迭代实现。

    实战2:递归实现数组元素求和(数组核心应用)

    需求:结合数组、函数、循环、变量知识点,用递归实现整型数组所有元素求和,全程不使用指针,适配基础学习范围。 思路:定义数组、记录数组下标,逐个累加当前下标元素,下标向后递增,直到下标超出数组长度终止递归,拆分累加子问题实现整体求和。

    #include <stdio.h>

    // 递归数组求和:arr数组、len数组长度、index当前遍历下标
    int arraySum(int arr[], int len, int index)
    {
    // 终止条件:下标超出数组最大下标,无元素可加,返回0
    if (index >= len)
    {
    return 0;
    }
    // 当前元素 + 剩余数组元素的和
    return arr[index] + arraySum(arr, len, index + 1);
    }

    int main()
    {
    // 定义数组(核心知识点:数组)
    int arr[6] = {10, 20, 30, 40, 50, 60};
    int len = 6;
    int sum = arraySum(arr, len, 0);
    printf("数组所有元素之和:%d\\n", sum); // 输出210
    return 0;
    }

    实战3:青蛙跳台阶问题(选择+递归经典面试题)

    题目:一只青蛙一次可以跳1级台阶,或2级台阶,求跳上n级台阶共有多少种跳法? 思路分析:

    • 跳上n级台阶,最后一步只有两种可能:跳1级 或 跳2级
    • 总跳法 = 跳n-1级的方法数 + 跳n-2级的方法数(和斐波那契数列逻辑一致)
    • 终止条件:n=1(1种)、n=2(2种)

    #include <stdio.h>

    int jump(int n)
    {
    if (n == 1) return 1;
    if (n == 2) return 2;
    return jump(n 1) + jump(n 2);
    }

    int main()
    {
    printf("跳5级台阶共有%d种跳法\\n", jump(5)); // 输出8
    return 0;
    }

    五、递归核心避坑指南(新手必看)

    递归看似简洁,但坑点极多,整理5个高频问题,彻底规避bug:

  • 必须写终止条件:没有出口一定死递归,导致栈溢出,这是最基础也最多人犯的错误。
  • 每次递归必须缩小问题规模:不能出现参数不变、规模不变的递归,否则永远无法触发终止条件。
  • 控制递归深度:系统栈空间有限,递归层数过多(上千层)会直接栈溢出,复杂场景优先迭代。
  • 规避重复计算:斐波那契、跳台阶等双分支递归,重复计算极多,大数据量下禁止用递归。
  • 不盲目使用递归:递归的优势是代码简洁、逻辑清晰;劣势是开辟栈帧、占用内存、效率略低。适合数组拆分、数值迭代、分治类问题,简单循环场景优先迭代。
  • 六、递归通用解题模板(直接套用)

    最后给大家总结一个万能递归书写模板,新手写递归直接套框架,零出错:

    返回值类型 递归函数(参数)
    {
    // 1. 递归出口:最简情况,直接返回结果
    if (终止条件成立)
    {
    return 结果;
    }
    // 2. 问题拆分:缩小规模,递归调用
    // 3. 结果汇总:回溯计算,返回最终结果
    return 递归逻辑;
    }

    七、总结

    递归的核心从来不是「函数调用自己」,而是拆分问题、收敛规模、边界终止、回溯汇总。 新手学习递归不要死磕代码,先理清三个问题:

  • 什么时候停止?(终止条件)
  • 怎么拆分问题?(递推逻辑)
  • 怎么汇总结果?(回溯逻辑)
  • 掌握本文的基础案例+实战真题,你就已经吃透了C语言递归90%以上的场景,后续复杂的二叉树遍历、分治算法、回溯算法,都是递归思想的延伸。

    课后练习:1、用递归实现整数逆序输出;2、用递归求数组最大值,巩固递归+数组、选择语句核心知识点!

    赞(0)
    未经允许不得转载:171主机测评 » 函数递归从入门
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址