剑指 Offer 10- I. 斐波那契数列(leetcode)
前言
斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:
0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,....
这个数列从第3项开始,每一项都等于前两项之和。
- F(0) = 0
- F(1) = 1
- F(n) = F(n - 1) + F(n - 2),其中n > 1
因为在计算的过程中 有很多重复计算的过程,所以可以进行缓存处理
动态规划
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。
可能有很多解,每个解对应一个值, 我们希望找到最优解。基本思想就是待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解
将一个大的问题拆分成一个个子问题,我们把它称之为子结构。
每个最优解,也就是最优值均由[这些小规模子问题]推到而来。
更重要的就是利用历史记录,来避免我们重复的计算。
- 定义状态 dp数组 ,明确dp[i]代表的含义
- 列出状态转移方程 dp元素之间的关系
- 初始化状态 确定dp元素中的最开始元素值
剑指offer 10-I. 斐波那契数列
解法1: 递归调用
function fib(n: number): number {
const _fib=(n)=>{
if(n<=1){
return n
}
if(n>1){
return (_fib(n-1)+_fib(n-2))% 1000000007
}
}
return _fib(n)% 1000000007
};
为什么答案需要取模呢
该解法是最直接和暴力的想法 , 但是有一个问题,这里循环调用很多次容易引起栈溢出,优化方法采用动态规划和尾递归
优化后的递归实现
function fib(n: number): number {
if(n<=1){
return n
}
let res1 = 0,res2 = 1;
for(let i=2;i<=n;i++){
[res1,res2] = [res2% 1000000007,(res2+res1)% 1000000007]
}
return res2
};
解法2: 动态规划
function fib(n: number): number {
let dp:Array<number> = new Array(n+1);
if(n<1){
return n
}
dp[0]=0,dp[1] =1;
for(let i=2;i<=n;i++){
dp[i] = (dp[i-1]+dp[i-2])% 1000000007
}
return dp[n]
};
注意: 需要当心 第一个值多少,第二个值是多少,要根据题目确定