递归的精华是一递一归。所谓递,就是不断嵌套函数;所谓归,就是逐个将值返回。递而不归,就会越嵌套越深,直至突破内存极限而出错。
递归函数的定义有两个方面:
- 不断调用自己本身(只满足这个条件的是死递归)
- 有明确的结束条件
例如,下面的这个函数就是一个死敌归:
1 | def func(): |
程序并没有一直运行,输出1,而是运行到一定深度(层次)后就停止了。
这是因为 Python 为了保护计算机而设置了递归的深度限制。官方声明的限制是 1000 层,但实际测试往往在 998/997 层左右。
我们也可以修改系统设置的迭代深度限制:
1 | import sys |
现在,让我们用递归写一个阶乘的函数:
1 | def factorial(n): |
递归的思路是:找到 f(n)
与 f(n - 1)
之间的关系,然后将这种关系作为返回值或者其他操作。通过设置起始位置或终止位置的函数值实现函数的结束条件。
对于上个例子来说,factorial(n)
与 factorial(n - 1)
之间的关系是 factorial(n) = factorial(n - 1) * n
。而当 n
为 1
时,factorial(1) = 1
。
把上面的例子拆开看就是下面这个样子:
函数层数 | n | 返回值 |
---|---|---|
factorial(5) | 5 | factorial(4) * 5 |
1 | 4 | factorial(3) * 4 * 5 |
2 | 3 | factorial(2) * 3 * 4 * 5 |
3 | 2 | factorial(1) * 2 * 3 * 4 * 5 |
4 | 1 | 1 * 2 * 3 * 4 * 5 |
有些问题使用递归解起来会有很奇妙的感觉,比如解决汉诺塔问题等。
但是因为层层嵌套,层层调用,递归非常占用内存,运行速度也相对缓慢。使用尾递归可以略微缓解这个麻烦,但也是慢,而且很难。