斐波那契数列模型

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.第 N 个泰波那契数
题目链接:1137. 第 N 个泰波那契数
题目分析:

返回第n个泰波那契数 Tn 的值。
n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2,这个公式可以转化一下看的更明白:
Tn = Tn-3 + Tn-2 + Tn-1, Tn等于前面三个数之和。T0,T1,T2已经给我们了。

接下来用动态规划的思想来解决这个问题。
算法原理:
动态规划思想有5步:
- 先确实状态表示
- 根据状态表示推导状态转移方程
- 初始化
- 填表顺序
- 返回值
动态规划做题流程一般是 先定义一个dp表,这个表可能是一维数组也可能是二维数组。然后想办法把这个dp表填满,里面某个位置的值就是我们的最终结果!
下面具体解释5步:
1.状态表示
是什么 ?dp表中某个位置的值代表什么含义
怎么来的?
- 题目要求
- 经验+题目要求
- 分析问题的过程中,发现重复子问题
比如这道题就可以根据题目要求来得到状态表示。你要返回第n个泰波那契数,那我让dp[0]表示第个泰波那契数,dp[1]表示第个泰波那契数,那最后返回dp[n]就行了。
dp[i]表示:第 i 个 泰波那契数

2.状态转移方程
dp[i] 等于什么这个推导公式就是状态转移方程。
我们要想办法让之前的状态或者之后的状态来表示dp[i]。
这个题就已经告诉我们状态转移方程了,Tn就等于前三个泰波那契数和。dp[i]依赖前三个,并且是它们的和。dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。
dp[i] 等于什么这个推导公式只能就题论题了!

3.初始化
保证填表的时候不越界
我们做动态规划就是为了把dp表填满,填表的时候不越界的意思是,我们要先知道怎么填表,就是根据状态转移方程填表。
就比如这道题我想填dp[4],我仅需要知道前三个位置的值就可以填dp[4]了。
那为什么要保证不越界呢?
比如这个0、1、2这个位置,如果用状态转移方程来填这些位置的时候,比如0带进去出现-1、-2、-3,这个数组不能访问这些位置越界了!
因此用状态转移方程填表的时候必须要保证不越界的!
比如这道题前三个位置越界,我仅需要把前三个位置初始化。这道题也告诉我们了。

4. 填表顺序
为了填写当前状态的时候,所需要的状态已经计算过了。
比如初始化完dp[0],dp[1],dp[2],直接去填dp[4],需要知道前三个位置的值,但是现在并不知道dp[3]位置的值是多少。因此填表的时候必须要规定一个顺序。这道题就是从左往右填。

5.返回值
结合题目要求+状态表示
这道题让返回第n个泰波那契数,我们的状态表示第i个泰波那契数。因此直接返回dp[n]就行了。

只要完成这五步,动态规划算法原理就结束了。
动态规划编写代码就固定四步:
- 创建dp表
- 初始化
- 填表
- 返回值
class Solution {
public:
int tribonacci(int n) {
// 1.创建dp表
// 2.初始化
// 3.填表
// 4.返回值
// 处理一些边界情况
if(n == 0) return 0;
if(n == 1 || n == 2) return 1;
vector<int> dp(n)
dp[0] = 0, dp[1] = dp[2] = 1;
for(int i = 3; i <= n; ++i)
{
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
}
return dp[n];
};
简单分析一下时间复杂度O(N),空间复杂度O(N)。
接下来学习一下空间优化的技巧。
动态规划的空间优化一般都是用滚动数组方式来优化的。
比如说这道题,我求dp[3] 需要 dp[0]、dp[1]、dp[2]三个位置,dp[4] 需要 dp[1]、dp[2]、dp[3]三个位置 等等。

有没有发现当我们在求某一个位置的值时仅需要知道前面三个状态的值就可以了。比如dp[4]用不到dp[0],dp[5]用不到dp[0]、dp[1],那求其他位置的时候这些用不到的位置就浪费空间了。

当我们再填dp表的时候,求dp[i]的时候,前面一些状态就可以丢去,仅需要它前面若干个状态就可以了,像这样一种情况都可以用滚动数组来做优化!
优化后O(N^2)->O(N),O(N)->O(1)。
如何优化?
仅需要几个变量就可以了。
比如这道题求某个位置的值需要知道前面三个位置状态的值,因此需要a、b、c、d四个变量就行了。a、b、c记录前三个位置状态值,d记录当前求得位置得状态值。初始的时候a = 0 ,b = 1, c = 1 ,d在dp[3]。比如算完dp[3],然后让a、b、c、d滚动一下,算dp[4] 。像这样的技巧就是滚动数组。

这里有个细节问题。滚动就是要完成赋值操作。相当于b的值给a,c的值给b,d的值给c。现在有两种赋值操作,从前向后赋值还是从后像前赋值?
第一种方式是对的,因为第二种方式赋值完都是a、b、c都是d!

class Solution {
public:
int tribonacci(int n) {
// 1.创建dp表
// 2.初始化
// 3.填表
// 4.返回值
// 处理一些边界情况
if(n ==

最低0.47元/天 解锁文章
2898

被折叠的 条评论
为什么被折叠?



