菲波那切数列的顺序是 0,1,1,2,3,5,8,13,21,….。
递归形式
按照我们以前的思路,很容易就想到递归来做。这里以 fib(6) 来展开得到的结果如下,它的一个时间复杂度是 O(2n)。
代码形式如下。
function fib(n) {
if(n <=0) {
return 0
} else if(n == 1) {
return 1
} else {
return fib(n-1) + fib(n-2)
}
}
// 简化形式如下
function fib(n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
记忆化
但是从上面也可以看出很多重复计算,比如 fib(4) 被计算了两次,这时我们可以通过记忆化的方式,也就是通过一个数组把结果缓存起来,当有缓存(memo有值)时直接取缓存结果,没有时才去计算。代码如下。
function fib(n, memo = []) {
if(n <=0) {
return 0
} else if(n == 1) {
return 1
} else if(memo[n]) {
memo[n] = fib(n-1) + fib(n-2)
}
return memo[n]
}
可以看出,通过这种形式,每个节点只被访问和计算了一次,从而达到时间复杂度优化到了 O(n)。
递推
接着就是把上面的例子转化为递推的形式,这里就不再是自上而下,而是反过来,从下往上计算,因为最下面的值就是初始值(fib[0]=0, fib[1]=1),而我们要做的就是不断的往上加。
它的一个递推公式是 F[n] = F(n-1) + F(n-2)。伪代码如下。
F[0]=0, F[1] = 1
for(var i=2; i<=n; ++i) {
F[i] = F[i-1] + F[i-2]
}
function fib(n) {
var memo = [], memo[0]=0, memo[1]=1
for(var i=2; i<=n; ++i) {
memo[i] = memo[i-1] + memo[i-2];
}
return memo[n]
}