[C语言初阶]递归

上一篇文章我们详细介绍了C语言中函数的基础内容。在这一节中,我们讲来继续深入学习一种函数中好用的技巧——递归。 

[C语言初阶]递归_第1张图片

目录

一、递归的定义

二、栈溢出原理

2.1 内存分区示意图:

三、递归的必要条件

3.1 示例:打印数字每一位

四、递归的经典应用

4.1  求字符串长度(模拟strlen)

4.2.  汉诺塔问题

4.3. 青蛙跳台阶(斐波那契数列变种)

五、递归与迭代的对比

5. 1 示例:斐波那契数列


一、递归的定义

递归是函数通过调用自身来解决问题的一种编程技巧。它的核心思想是“大事化小”,即将一个复杂的问题分解为多个与原问题相似但规模更小的子问题,直到问题简化到可以直接解决。

例如,计算阶乘 n! 的递归实现:

int factorial(int n) {
    if (n == 0) return 1;          // 终止条件
    return n * factorial(n - 1);   // 递推关系
}

这里,n! 被分解为 n * (n-1)!,直到 n=0 时终止递归。


二、栈溢出原理

递归调用依赖内存中的栈区存储函数调用信息。每次递归调用都会在栈中分配一块空间(称为栈帧),用于保存局部变量和返回地址。若递归深度过大,栈空间会被耗尽,导致栈溢出(Stack Overflow)

2.1 内存分区示意图:

[C语言初阶]递归_第2张图片

提示:递归深度需谨慎控制,避免无限递归。例如,缺少终止条件的斐波那契递归会迅速导致栈溢出。


三、递归的必要条件

递归必须满足两个核心条件:

  1. 终止条件:递归必须有一个明确的结束条件。
  2. 递推关系:每次递归调用后,问题规模应趋近于终止条件。

3.1 示例:打印数字每一位

void printDigits(int n) {
    if (n > 9) {               // 终止条件:n为个位数时不再递归
        printDigits(n / 10);   // 递推:去掉最后一位
    }
    printf("%d ", n % 10);     // 打印当前最后一位
}

输入 1234,输出 1 2 3 4。每次递归调用 n 的规模缩小,直到 n <= 9

思路:从最简单的问题解决,哪个容易就解决哪个,然后拆成多个相似的规模小的问题,思路类似于倒推法+拆解,不理解就画图可视化,递归完后从哪里来,回哪里去


四、递归的经典应用

4.1  求字符串长度(模拟strlen

int myStrlen(const char* str) {
    if (*str == '\0') return 0;                // 终止条件:空字符
    return 1 + myStrlen(str + 1);              // 递推:指针后移一位
}

关键点:递归到字符串末尾的空字符 \0 时终止。

4.2.  汉诺塔问题

void hanoi(int n, char A, char B, char C) {
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", A, C);
        return;
    }
    hanoi(n - 1, A, C, B);    // 将n-1个盘子从A移到B(借助C)
    printf("Move disk %d from %c to %c\n", n, A, C);
    hanoi(n - 1, B, A, C);    // 将n-1个盘子从B移到C(借助A)
}

思路:将问题分解为移动 n-1 个盘子和移动最底层的盘子,底层是分步乘法原理。

4.3. 青蛙跳台阶(斐波那契数列变种)

青蛙一次可以跳1或2阶台阶,问跳上 n 阶有多少种方法:

int jump(int n) {
    if (n <= 2) return n;     // 终止条件:1阶1种,2阶2种
    return jump(n - 1) + jump(n - 2);  // 递推:最后一步跳1或2阶
}

思路:分类讨论最后一步为一步和两步的情况,底层是分类加法原理。 


五、递归与迭代的对比

递归 迭代
代码简洁,符合直觉 代码稍复杂,需手动管理循环变量
可能栈溢出,效率低(如斐波那契) 无栈溢出风险,效率高
适合分治问题(如汉诺塔) 适合线性计算(如阶乘、累加)

5. 1 示例:斐波那契数列

// 递归(效率低)
int fib(int n) {
    if (n <= 2) return 1;
    return fib(n-1) + fib(n-2);
}

// 迭代(效率高)
int fibIter(int n) {
    int a = 1, b = 1, c;
    for (int i = 3; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

总结:如果功能实现用递归求解简单,容易理解,并且写完后无明显缺陷,用递归解决,而如果用递归写有缺陷(如栈溢出,效率低),则用非递归的迭代实现 


递归是C语言中一种强大的工具,其核心在于将复杂问题拆解为相似子问题。本文我们通过对递归的经典案例(如汉诺塔、阶乘)的练习,可以深入理解递归的思维模式。然而,递归并非万能,需结合实际场景权衡其与迭代的优劣。在下篇文章中,我们将进行C语言中数组的学习,敬请期待!

作者其他文章链接:

初识C语言—人生若只如初见(持续更新中!)-CSDN博客

[C语言初阶]分支和循环语句练习-CSDN博客

[C语言初阶]猜数字小游戏实现-CSDN博客

Gitee详细使用教程_gitee 教程-CSDN博客

[C语言初阶]函数-CSDN博客

你可能感兴趣的:(c语言,算法,数据结构)