信奥竞赛

系列

剑指信奥 | C 语言之递归算法

经典的算法

在之前我们的 Scratch 课程中,曾经讲过一个经典的算法,这就是递归:

和 Vicky 一起学习 Scratch 系列之二十 —— 递归算法

递归是一个很有用的算法,在很多情况下,可以把看似复杂的问题,简洁巧妙的解决,递归也是各种复杂算法的基础,我们看一个小的例子:

求和

接收用户输入的自然数 n,用递归的方式求 1 到 n 的和。

我们先定义一个求和的函数:


int sum(int n) {
    if (n == 1) {
        return1;
    } else {
        return n + sum(n - 1);
    }
}
                

然后,在主函数中调用这个求和函数:

#include <stdio.h>
int sum(int n) {
    if (n == 1) {
        return1;
    } else {
        return n + sum(n - 1);
    }
}
int main() {
    int n;
    scanf("%d", &n); // 输入 3
    printf("%d", sum(n)); // 输出 6
}
                

输入 3,1+2+3 的结果就是 6。

而这,就是一个递归函数的简单例子。

什么是递归

递归 recursion 在计算机科学中的定义是这样的:

递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。

Wikipedia

定义说的很清楚,递归就是在函数内部调用函数自身的情况,看看我们刚刚写的代码:

#include <stdio.h>
int sum(int n) {
    if (n == 1) {
        return1;
    } else {
        return n + sum(n - 1);
    }
}
int main() {
    int n;
    scanf("%d", &n); // 输入 3
    printf("%d", sum(n)); // 输出 6
}
                

这里面哪一行体现了递归呢?

或许你已经发现了,就是这一句:

return n + sum(n - 1); // 递归的体现

为什么?

因为这条语句位于函数 sum() 之内,而同时它又调用了 sum() 函数本身,这正是递归的具体体现 —— 在函数的内部调用函数自身。

递归的优缺点

之前我们提到了,递归算法的优势在于可以很便捷的解决一些看似很复杂的问题。

比如,我们之前在 Scratch 的递归课中,使用递归算法很简单的就画出了一棵看似很复杂的树:

Scratch 实现的递归树

但是递归算法并非没有缺点,它的一个明显的问题是,递归算法的的性能比较低,如果比较看重这一点,还是建议使用循环来替代递归。

此外,太深层次的递归不仅慢,受到内存的限制,还有可能发生 栈溢出 stack overflow 的运行错误。

递归虽好,也要有选择的使用。

C 语言的总结

好啦,同学们,我们的 C 语言到目前为止已经完成了十多次课,现在我们来回顾一下,我们都讲了哪些内容:

  1. C 语言的历史背景和编译环境
  2. C 语言的数据类型和变量
  3. C 语言 I/O 和信奥试题模式
  4. C 语言的运算符和选择结构
  5. C 语言的 for 以及 while 循环
  6. C 语言的文件读写操作方法
  7. C 语言数组的定义和操作
  8. C 语言的字符串定义和使用
  9. C 语言的函数和自定义函数
  10. C 语言中递归算法的实现

到目前为止,关于 C 语言的理论内容我们已经基本完成了,在接下来的几次课中,我们会具体利用 C 语言,讲解一些典型的信息学竞赛试题,把理论融入于实践,可以更加深刻的体会到 C 语言本身的强大和魅力!

日积月累

  • recursion 递归
  • stack overflow 栈溢出