目录
- 斐波那契数列
- 递归法
- 尾调用优化
- 高级函数
- 通过map记忆优化
- 动态规划
- 循环
一、斐波那契数列
首先,斐波那契数列从第0个开始,分别是
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
因此要根据该规则,返回第n个斐波那契数
斐波那契数,通常使用 F(n)
表示。
形成的序列成为 斐波那契数列。
该数列从 0
和 1
开始,后面的每一项数字都是前面两项数字的和。
也就是说:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n
,请计算 F(n)
。
示例 1:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:
输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:
输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3
二、递归法
- 时间复杂度: O(2^n)
上次写斐波那契数列已经是18年的事情了,时间过的很快,一直忙于业务,算法是时候深入了
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
if(n === 1 || n === 0 ) return n;
return fib(n-1) + fib(n-2);
};
递归的思路很简单,即不断调用自身方法,直到n为1或0之后,开始一层层返回数据。
使用递归计算大数字时,性能会特别低,原因有以下2点:
① 在递归过程中,每创建一个新函数,解释器都会创建一个新的函数栈帧,并且压在当前函数的栈帧上,这就形成了调用栈。因而,当递归层数过大之后,就可能造成调用栈占用内存过大或者溢出。 ② 递归造成了大量的重复计算。
递归的以上两种缺点,我们可以使用尾调用优化和递推法来解决。
三、尾调用优化
尾调用是指一个函数里的最后一个动作是一个函数调用的情形:即这个调用的返回值直接被当前函数返回的情形。WikiPad[1] 用代码来说,就是B函数的返回值被A函数返回了。\
1) 举例子
function B() {
return 1;
}
function A() {
return B(); // return 1
}
2) 斐波那契数列
var fib = function(n, current, next) {
if(n === 1) return next;
if(n === 0) return 0;
return fib(n - 1, next, current + next);
};
fib(7, 0, 1); // 13
四、高级函数
var fib = function(n) {
let seed = 1;
return [...Array(n)].reduce(p => {
const temp = p + seed;
seed = p;
return temp;
},0)
};
fib(6);
五 记忆 来优化,斐波那契数列
优化添加了 记忆 , 也就是缓存了之前计算过的数列的值,再次计算的时候直接返回值即可。
const map = new Map()
var fib = function(n) {
if(n < 2) return n
if(!map.has(n)) {
map.set(n, fib(n - 1) + fib(n - 2))
}
return map.get(n)
}
fib(6); // 8
六 动态规划
var fib = function(n) {
let dp = [0, 1]
for(let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2]
}
console.log(dp)
return dp[n]
};
// fib(6);//8
// [0, 1, 1, 2, 3, 5, 8]
七 循环
- 时间复杂度: O(n)
- 空间复杂度: O(n)
```js
function fib (n) {
let step = []
step[1] = 1
if ( n >= 2) {
step[2] = 2
}
if ( n <= 2) {
return n
}
for ( let i = 3; i <= n; i++) {
step[i] = step[i - 1] + step[i - 2]
}
return step[n-1]
};
fib(6); // 8
参考
总结
- 斐波那契数数的值就是它后两位的值的和,学会使用 记忆 来优化代码
- 求出f(n),只需要知道几个更小的f(c)。我们将求解f(c)称作求解f(n)的“子问题”, 这就是DP(动态规划,dynamic programming).将一个问题拆成几个子问题,分别求解这些子问题,即可推断出大问题的解。
- 斐波那契数列 既可通过递归实现,又可通过循环实现