1. 题目来源
509. 斐波那契数
这题的要求是0 <= n <= 30 (用无脑递归可以通过)
剑指 Offer 10- I. 斐波那契数列
这题的要求是0 <= n <= 100 (用无脑递归不可以通过)
2. 题目描述
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。
斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
3. 题目解析
3.1 暴力递归
就直接暴力递归,虽然能解决问题,但是数字一大复杂度就特别高。
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
if( n === 0) return 0
if(n === 1) return 1
return fib(n-1) + fib(n-2)
};
这种无脑解法可以初步解决问题,但是我们仔细思考一下,他的优化空间很大
首先一个问题就是这里重复递归的次数实在是太多了,比如说我要求fib(5),
如图可视,我们重复求解了多次fib(2)和fib(3)其实这是没有必要的,我们可以用一个缓存cache,来将我们求过了的fib(n)保存在缓存中,下次用到他的时候直接读取他的值就可以了,而不是再重新递归求值,
3.2 递归 + 缓存
/**
* @param {number} n
* @return {number}
*/
// 首先定义一个缓存数组,用来存放求出来的Fib(k)的值 k = 2, ... , n
let cache = []
var fib = function(n) {
// 1. 如果缓存中已经有这个值了,就直接返回,不要再递归求值了
if(cache[n] !== undefined){
return cache[n] }
// 两个初始值
if(n === 0) return 0
if(n === 1) return 1
// 递归求得fib(n)
let v = (fib(n-1) + fib(n-2)) %1000000007
// 将fib(n)保存在cache[n]的位置
cache[n] = v
// 返回fib(n)
return v
};
3.3 动态规划
加了缓存的递归虽然可以通过测试了,减少了重复的递归计算,但是利用了额外的空间来存储缓存,还有进一步优化的空间
var fib = function(n) {
if(n<2) return n;
let i = 0;
let j = 1;
let sum = 1;
// 每循环一次,将i和j的值相加得到sum放在j后面,将i和j都后移一位
for(let k = 2; k <= n; k++){
i = j;
j = sum;
// sum 是i和j的和 保存在i和j的后面
sum = (i + j) % 1000000007;
}
return sum ;
};
3.4 矩阵快速幂
Leetcode上面的题解!!!扩展思路!!
计算出 M n M^n Mn 之后,