2-【斐波那契数列模型】LeetCode1137-第N个泰波那契数

题目

泰波那契序列 Tn 定义如下: 

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2。

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例 1:

输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

示例 2:

输入:n = 25
输出:1389537

提示:

  • 0 <= n <= 37
  • 答案保证是一个 32 位整数,即 answer <= 2^31 - 1。

思路

由题目所给:泰波那契序列 Tn 的定义: T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2。

可得Tn = Tn-3 + Tn-2 + Tn-1。

  • T0 = 0
  • T1 = 1
  • T2 = 1
  • T3 = 2
  • T4 = 4
  • T5 = 7

......

做题流程:

  1. 动态规划一般会先定义dp表:一维数组/二维数组。
  2. 想办法将dp数组填满。
  3. 填满后dp数组中的某一个值就是最终结果。

step1:状态表示(确定dp数组以及下标含义)

①是什么?

状态表示就是dp表中某一个值所表示的含义。

②怎么来?

  1. 题目要求。
  2. 经验(大量做题)+题目要求。
  3. 在分析问题的过程中发现重复子问题,把这个重复子问题抽象成一个状态表示。

是最重要的一步!

本题中直接根据题目要求:

  • 让dp[0]表示第0个泰波那契数的值
  • 让dp[1]表示第1个泰波那契数的值
  • 让dp[2]表示第2个泰波那契数的值
  • ......
  • 让dp[i]表示第i个泰波那契数的值
  • 最后返回dp[n],即为第n个泰波那契数的值

step2:状态转移方程(确定递推公式/状态转移公式)

①是什么?

状态转移方程就是dp[i]等于什么?

②怎么来?

就题分析~

是最难的一步!

本题中直接得出:dp[i] = dp[i -1] + dp[i -2] + dp[i -3] 

step3:初始化(dp数组如何初始化)

①含义?

初始化就是保证填表的时候不越界

②怎么填表?

就是根据状态转移方程来进行填表!

若是:

  • dp[0] = dp[-1] + dp[-2] + dp[-3]
  • dp[1] = dp[0] + dp[-1] + dp[-2]
  • dp[2] = dp[1] + dp[0] + dp[-1]

其中dp[-1]、dp[-2]、dp[-3]越界,无法访问。

so:dp数组的前3个位置会越界访问,那么就需要将前3个位置进行初始化。那么之后的位置就不会出现越界访问问题了。

本题中直接得出:dp[0] = 0、dp[1] = 1、dp[2] = 1。

step4:填表顺序(确定遍历顺序)

确定填表顺序是为了在填写当前状态的时候,所需要的状态已经计算过了。

本题中的填表顺序是:从左到右。

step5:返回值(举例推导dp数组)

题目要求+状态表示

本题中返回值是:dp[n]。


代码1

class Solution {
    public int tribonacci(int n) {
        //处理边界情况
        if(n == 0) {
            return 0;
        }
        if(n == 1 || n ==2) {
            return 1;
        }

        //1.创建dp表
        int[] dp = new int[n + 1];

        //2.初始化
        dp[0] = 0; 
        dp[1] = dp[2] = 1;

        //3.填表
        for(int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2] + dp[i -3];
        }

        //4.返回值
        return dp[n];
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

空间优化

关于动态规划问题的空间优化,一般都是使用滚动数组。

  • 在依次向后求dp[i]时,前面的一些不用的状态可以舍弃,仅仅用中间有效的若干的状态即可。
  • 这样的情况都可以用滚动数组来做优化!
  • 设置对应数量的变量,利用这些变量求出下一个值,依次将这些变量向后滚动一位(赋值操作),继续求下一个值,以此类推。

PS:赋值操作有两种实现顺序:

  1. 从前向后(正确)
  2. 从后向前(错误,之前的值会被覆盖)

优化结果:

  • 之前是O(n ^ 2) -> 优化后是O(n)
  • 之前是O(n) -> 优化后是O(1)(仅用几个有限的变量即可)

本题中:

  1. 在求dp[4]时只用到dp[1]、dp[2]、dp[3],没有用到dp[0]。
  2. 在求dp[5]时只用到dp[2]、dp[3]、dp[4],没有用到dp[0]、dp[1]。
  3. ......
  4. 在求dp[i]时只用到dp[i - 1]、dp[i - 2]、dp[i - 3]。
  • 那么只需要设置a,b,c三个变量即可,让这三个变量开始时a = dp[0] = 0,b  = dp[1] = 1,c =dp[2] = 1。
  • 用这三个变量更新完变量d = dp[3]后,将a、b、c、d四个变量依次向右滚动一位。
  • 赋值操作顺序:
  1. 从左向右:a = b,b = c,c = d(正确)。
  2. 从右向左:c = d,b = c,a = b(错误,之前的值会被覆盖)。
  • 同理可以利用a、b、c求得d,即dp[4]、dp[5]......

代码2

class Solution {
    public int tribonacci(int n) {
        //处理边界情况
        if(n == 0) {
            return 0;
        }
        if(n == 1 || n ==2) {
            return 1;
        }

        int a = 0, b = 1, c = 1, d = 0;

        for(int i = 3; i <= n; i++) {
            d = a + b + c;
            //滚动操作
            a = b;
            b = c;
            c = d;
        }

        return d;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值