斐波那契数列的几种解法

斐波那契数列一般都用于介绍递归的思想。

我们知道斐波那契数列的通项公式(n>1)如下:

F(n) = F(n-1) + F(n-2)

按照这个公式写个代码就很容易了:

int fibonacci(int n) {
    if (n == 0 || n == 1) {
        return n;
    }
    return fibonacci(n-1) + fibonacci(n-2);
}

这种代码简单又优雅,但是缺点也很明显,就是慢:

又慢又占空间。

这是为什么呢?

我们来看看递归都做了什么,以n=5为例,如下图:

这里会有很多的重复计算过程,这个重复计算的过程叫做递归栈,递归最大的毛病就是递归栈里重复的东西太多。

观察上图,如果从下向上,效果会更加的好,可以借助数组来实现,我们知道斐波那契数列其实就是这样的一个数列:

0,1,1,2,3,5,8...

只要将第一和第二项算出来保存在数组中,后面的项就很好计算了:

public int Fibonacci2(int n) {
        int[] fib = new int[45];
        fib[0] = 0;
        fib[1] = 1;
        for (int i = 2; i <= n; i++) {
            fib[i] = fib[i - 1] + fib[i - 2];
        }
        return fib[n];
    }

n=5的时候,这个数组最终是这样的[0,1,1,2,3,5,......],此时的时间复杂度仅仅是O(n),比递归的O(2^n)好太多了:

仅仅消耗了17ms,是原先的5.8%左右。

但是,继续观察上面的数组:

[0, 1, 1, 2, 3, 5],我们会发现这个数组中,f(0)和f(2)并没有用到,这两个其实可以精简掉,于是代码变成了这样:

public int Fibonacci(int n) {
        if (n == 0 || n == 1) {
            return n;
        }
        int a = 0;
        int b = 1;
        int c = 0;
        for (int i = 2; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
    }

n=5的情况下,这个循环只会计算f(1),f(3),f(4)和f(5)的值,并不用数组去保存,因此空间也节省了下来。

另外我试了试C和Java语言用第三种解法的速度差异,发现C语言还是很有优势的:

同样的算法竟然只用了5ms。

posted @ 2021-08-12 23:44  wingsless  阅读(918)  评论(0编辑  收藏  举报