[极客时间-每日一课]如何优雅地计算斐波那契数列?

课程:https://time.geekbang.org/dailylesson/detail/100028406javascript

问题:计算斐波那契数列的第n项的值,数列表达式:F[n]=F[n-1]+F[n-2] (n>=2,F[0]=0,F[1]=1)html

winter(讲师)认为这是一道很好的面试题,java

  1. 答案简单。面试

  2. 每一个面试者都能写出点东西。数组

  3. 区分度高,不一样水平的人写出来的代码水平不一样。less

  4. 马甲众多(不多有面试官直接上来讲给我撸一个斐波那契数列,都会问一个具体的问题,最后归根到底就是斐波那契数列问题)。函数

 

好,接下来看一下winter认为不一样水平的代码长啥样。性能

 

Lev1. 递归优化

int Fibnacci(int n){
    if(n < 2){
        return n;
    }
    return Fibnacci(n - 1) + Fibnacci(n - 2);
}

能够简单算一下它的时间复杂度是指数级的。它会把子问题重复计算多遍。若是我要计算数列第5项的值,会计算以下中间结果。spa

 

 

 能够观察到纯在大量重复的节点计算。优化一下。

 

Lev2. 带备忘录的递归

int Fibnacci(int n){
    if(map.ContainsKey(n)){
        return map[n];
    }
    if(n < 2){
        return n;
    }
    int res = Fibnacci(n - 1) + Fibnacci(n - 2);
    map.Add(n, res);
    return res;
}

时间复杂度O(n), 空间复杂度O(n)。到这有人说递归自带性能消耗,再优化一下。

 

Lev3. DP (动态规划)

当发现存在大量重复子问题的时候,一般咱们会想到DP.

首先咱们肯定状态转移方程 DP[n] = DP[n-1] + DP[n-2].

(DP是一种自下向上的解决问题的思路,先解出f(2), 那f(3)就得解,接着f(4)也就得解,直到f(n),而递归是自上而下)

int Fibnacci(int n){
    if(n < 2){
        return n;
    }
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    for(int i = 2; i <= n; i++){
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

时间复杂度O(n), 空间复杂度O(n)。

能够继续优化把DP数组去掉,空间复杂度优化成O(1),这里就不演示了。

到这,其实我认为这已是极限了,最起码是个人极限。

 

Lev4. 通项公式

没错,数学家给出了数列的通项公式,咱们老老实实套公式便可。

 

 

 感兴趣的能够本身推导一遍。

let fibnacci = (n) => ((Math.pow(1 + Math.sqrt(5))/2, n) - Math.pow((1 - Math.sqrt(5))/2, n))/Math.sqrt(5);  

问题来了,如今的时间复杂度是O(1)吗?严格意义上说不是,这里调用了系统的幂函数,winter没有指出该函数在V8的具体实现,可是结论必定不是O(n), 更不是O(1)

后面他提供了本身实现的O(log(N))幂运算版本:

let pow = (x, n) => {
    var r = 1;
    var v = x;
    while(n) {
        if(n % 2 == 1){
            r *= v;
            n-= 1;
        }
        v = v * v;
        n = n /2;
    }
    return r;
}

Lev N: ????

考虑到浮点偏差,winter再次提出借助线性代数的矩阵运算来表示斐波那契数列的通项。

到这里,我已经完全放飞自我,以为他说的都对。

 

总结

这节课的后半段,体验不是很好,毕竟数学知识全还了,思路已经跟不上了。

常见的斐波那契数列题型:

1. 爬台阶:有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次能够上1阶、2阶。实现一种方法,计算小孩有多少种上楼梯的方式(还有什么青蛙跳台问题,换一下主语)

2. 爬台阶+: 有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次能够上1阶、2阶、3阶。实现一种方法,计算小孩有多少种上楼梯的方式

3. 兔子繁殖问题: 一对兔子每月能生出一对小兔子来。若是全部兔子都不死,那么一年之后能够繁殖多少对兔子?

 

PS: 和斐波那契数列类似的著名数列:卡塔兰数列

具体问题:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

假如n=3,结果就是5种。

卡特兰的随笔已补:http://www.javashuo.com/article/p-ykkmuaye-cc.html

相关文章
相关标签/搜索