第6.11章: 递归函数

English

概述

递归函数是调用自身的函数。 递归是表达那些可以拆解为同形式更小子问题的自然方式。

在本章中,你将学到:

1. 什么是递归?

递归函数通过解决同一问题的更小版本来解决该问题。每个递归函数都必须有:

没有基准情形,递归永远不会结束,程序最终会因栈耗尽而崩溃。

2. 示例:阶乘

阶乘函数在数学上定义为:


n! = n × (n - 1)!  
0! = 1
      

这个定义天生就是递归的。


int factorial(int n)
{
    if(n < 0)
        ::fast_io::fast_terminate(); // factorial is undefined for negatives

    if(n == 0)
        return 1;                    // base case

    return n * factorial(n - 1);     // recursive case
}

每次调用都会缩小问题,直到抵达基准情形。

3. 递归如何使用调用栈

每次函数调用都会创建一个新的栈帧,其中包含:

递归函数每次递归调用都会创建一个栈帧。例如:


factorial(4)
 ├─ factorial(3)
 │   ├─ factorial(2)
 │   │   ├─ factorial(1)
 │   │   │   └─ factorial(0)

当基准情形返回时,栈按相反顺序展开。

如果递归太深,程序会因栈溢出而崩溃。

4. 尾递归

尾递归函数是指递归调用是函数中最后一步操作的函数。


int sum_tail(int n, int acc)
{
    if(n == 0)
        return acc;          // base case

    return sum_tail(n - 1, acc + n); // tail call
}

在某些语言中,尾递归会被优化成循环。然而:

C++ 并不保证尾调用优化。

编译器可能优化尾递归,但你不能依赖它。如果你需要保证栈用量的常量性, 请改写成显式循环。

5. 何时适合使用递归

递归在以下情况很合适:

递归在以下情况不合适:

6. 更多示例

6.1 斐波那契(低效的递归版本)


int fib(int n)
{
    if(n < 0)
        ::fast_io::fast_terminate();

    if(n <= 1)
        return n;

    return fib(n - 1) + fib(n - 2);
}

这个版本极其缓慢,因为它会反复重算相同的值。 迭代版或记忆化版本更好。

要点