关于递归

递归算法一般用于解决三类问题:
(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;
}

  

posted on 2018-07-15 22:25  areful  阅读(166)  评论(0编辑  收藏  举报

导航