问题描述
斐波那契数列 :1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 求第n个数是多少
大概的思路
- 递归
- 利用数组
- 动态规划
- 尾递归
四种写法
function fibonacci(num) {
if(num <=0 ){
return;
}
if( num === 1 && num ===2) {
return 1;
}
return fibonacci(num-1) + fibonacci(num-2);
}
function fibonacci2(num) {
let arr = [1, 1];
if(num <=0 ){
return;
}
if(num === 1 && num === 2) {
return 1;
}
for(let i = 2; i < num; i++) {
arr[i] = arr[i-1] +arr[i-2];
}
return arr[num-1];
}
function fibonacci3(num) {
var pre = 1;
var next = 1;
if(num <= 0 ){
return;
}
for(let i = 2; i < num; i++) {
let temp = pre;
pre = next;
next = pre + temp;
//或写成:
//next = pre + next;
//pre = next - pre;
}
return next;
}
function fibonacci4(n, a = 1, b = 1) {
if (n === 1) return a;
return fibonacci4(n - 1, b, a + b)
}
console.log(fibonacci(-1));
console.log(fibonacci2(10));
console.log(fibonacci3(10));
console.log(fibonacci4(10));
复制代码
几个问题?
-
什么是动态规划?
1.将一个原问题分解为若干个规模较小的子问题,递归的求解这些子问题
2.一个子问题在求解后,可能会再次求解,于是我们想到将这些子问题的解存储起来,当下次再次求解这个子问题时,直接拿过来使用。 -
复习时间、空间复杂度概念?
O(n): for循环里面的代码会执行 n 遍,因此它消耗的时间是随着 n 的变化而变化的,因此可以用O(n)来表示它的时间复杂度。
O(n²): 当存在双重循环的时候,即把 O(n) 的代码再嵌套循环一遍,它的时间复杂度就是 O(n²) 了。
O(logn) :在二分查找法的代码中,通过while循环,成 2 倍数的缩减搜索范围,也就是说需要经过 log2^n 次即可跳出循环。
O(nlogn): 将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logn),也就是了O(nlogn)。
总结: O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(n³)<O(2ⁿ)<O(n!)空间复杂度可以理解为除了原始序列大小的内存,在算法过程中用到的额外的存储空间。
-
递归算法的时间复杂度
如果递归函数中,只进行一次递归调用,递归深度为depth; 在每个递归的函数中,时间复杂度为T; 则总体的时间复杂度为O(T * depth)。 -
以上四种写法的时间复杂度和空间复杂度?
1.递归写法:
时间复杂度: O(2^n)
2^0 + 2^1 + 2^2 + ...... + 2^n = 2^(n+1) - 1 = O(2^n)空间复杂度:O(n)
n个叶子节点,n个调用栈2.利用数组:
时间复杂度: O(n) for一次循环
空间复杂度:O(n) 数组长度为n3.动态规划:
时间复杂度: O(n) for循环了一遍
空间复杂度:O(1)4.尾递归:
时间复杂度为 O(n) 计算执行了n次
空间复杂度为 O(1) 只有一个调用栈
总结:
斐波拉契数列,可以用几种方法来写,每个方法都有不同的时间、空间复杂度,当然还可以利用generator生成器等方法来写,我学的不好,就不写了。
本人才疏学浅,如有错误,还望指正。
参考文章:
从斐波那契数列谈谈代码的性能优化
冰与火之歌:「时间」与「空间」复杂度
看动画轻松理解「递归」与「动态规划」
JavaScript调用栈、尾递归和手动优化
尾调用和尾递归