递归算法一般用于解决三类问题:
(1)数据的定义是按递归定义的。(Fibonacci函数)
斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...,数列中从第三项起,每一项都是前两项之和。
(2)问题解法按递归算法实现。
这类问题虽则本身没有明显的递归结构,但用递归求解比迭代求解更简单,如Hanoi问题。
(3)数据的结构形式是按递归定义的。
如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归地描述。
请不要这样写递归:
#include<iostream> using namespace std; int F(int n)//函数返回一个数对应的Fibonacci数 { if(n==0 || n==1)//递归边界 return 1; return F(n-1) + F(n-2);//递归公式 } int main() { //测试 int n; while(cin >> n) cout << F(n) << endl; return 0; }
上述代码在计算比较大的数时,有严重的效率问题,可以算作逻辑bug了,为计算第40个斐波那契数,递归被调用了3亿多次,计算第50个斐波那契数,调用函数400多亿次……
问题出在这里:
.
return F(n-1) + F(n-2);
因为每次计算都需要在堆栈中缓存前两次的结果,导致堆栈使用急剧增大,很快就耗尽系统/虚拟机内存。总计算次数(正比于所需堆栈大小,可粗略估计为n!)可以写代码验证:
long total = 0; long a = 1; long b = 1; for (int i = 0; i <= 50; i++) { if (i < 2) { total += 1; continue; } long tmp = b; b = a + b + 1; a = tmp; cout << b << endl; }
为计算第50个斐波那契数,需要递归调用函数F()的次数是40730022147次。
这个例子告诉我们,在递归中调用自身超过一次都是有问题的,应尽力避免。
解决这个问题只需避免多余的出入栈操作,可参考下述代码,这样可以把所需堆栈大小降为正比于n:
#include<iostream> using namespace std; long a = 0; long b = 1; long fibs(int n) { if (n == 0 || n == 1){ return b; } long tmp = b; b = b + a; a = tmp; return fibs(n - 1); } int main() { cout << fibs(90) << endl; return 0; }