从0到1构建动态规划思维:斐波那契、爬楼梯与最小花费上楼技巧(509,70,746)

🤵‍♂️ 个人主页:@rain雨雨编程

😄微信公众号:rain雨雨编程

✍🏻作者简介:持续分享机器学习,爬虫,数据分析
🐋 希望大家多多支持,我们一起进步!
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+

目录

509. 斐波那契数

题目描述

思路步骤

代码实现

时间复杂度

空间复杂度

70. 爬楼梯

题目描述

思路步骤

代码实现

时间复杂度

空间复杂度

优化空间复杂度

优化后的代码实现

优化后的空间复杂度

746. 使用最小花费爬楼梯

题目描述

思路步骤

代码实现

时间复杂度

空间复杂度

优化空间复杂度

优化后的代码实现

优化后的空间复杂度


力扣难度
509. 斐波那契数🟢
70. 爬楼梯🟢
746. 使用最小花费爬楼梯🟢

509. 斐波那契数

题目描述

斐波那契数列是一个经典的数列,其中每一项都是前两项的和。数列的前两项分别是0和1,即F(0) = 0和F(1) = 1。对于任意的n > 1,第n项F(n)可以通过F(n-1) + F(n-2)计算得出。本题要求给定一个整数n,计算出第n项斐波那契数F(n)。

示例 1:

输入:n = 2 输出:1 解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

输入:n = 3 输出:2 解释:F(3) = F(2) + F(1) = 1 + 1 = 2

思路步骤

  1. 基本情况判断:如果n小于等于1,直接返回n,因为F(0) = 0和F(1) = 1。

  2. 动态规划数组初始化:创建一个长度为n+1的数组dp,用于存储斐波那契数列的值。dp[0]初始化为0,dp[1]初始化为1。

  3. 循环计算:从第2项开始,使用循环计算每一项的值,即dp[i] = dp[i-1] + dp[i-2]。

  4. 返回结果:循环结束后,返回dp[n]作为结果。

代码实现

class Solution {
    public int fib(int n) {
        // 基本情况判断
        if(n <= 1) return n;
        
        // 动态规划数组初始化
        int[] dp = new int[n + 1];
        dp[0] = 0; // F(0) = 0
        dp[1] = 1; // F(1) = 1
        
        // 循环计算斐波那契数列的值
        for(int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2]; // F(i) = F(i-1) + F(i-2)
        }
        
        // 返回第n项的值
        return dp[n];
    }
}

时间复杂度

时间复杂度为O(n)。这是因为我们需要从第2项开始,一直计算到第n项,每一项的计算时间是常数级别的,所以总的时间复杂度是线性的。

空间复杂度

空间复杂度为O(n)。这是因为我们使用了一个长度为n+1的数组来存储斐波那契数列的值。如果n很大,这个数组会占用相当大的内存空间。


70. 爬楼梯

题目描述

本题要求计算爬楼梯的不同方法数。假设有n阶楼梯,每次可以爬1阶或2阶,求到达楼顶的不同方法数。

示例 1:

输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶

  2. 2 阶

示例 2:

输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶

  2. 1 阶 + 2 阶

  3. 2 阶 + 1 阶

思路步骤

  1. 基本情况处理:对于n=0和n=1的情况,直接返回1,因为只有一种方法爬到楼顶。

  2. 动态规划数组初始化:创建一个长度为n+1的数组dp,用于存储爬到每一阶楼梯的方法数。dp[0]和dp[1]都初始化为1,因为到达第一阶和第二阶都只有一种方法。

  3. 循环计算:从第2阶开始,使用循环计算每一阶的方法数,即dp[i] = dp[i-1] + dp[i-2]。这是因为到达第i阶的方法可以从第i-1阶走一步上来,或者从第i-2阶走两步上来。

  4. 返回结果:循环结束后,返回dp[n]作为结果,即到达第n阶的方法数。

代码实现

class Solution {
    public int climbStairs(int n) {
        // 基本情况处理
        if (n <= 1) return 1;

        // 动态规划数组初始化
        int[] dp = new int[n + 1];
        dp[0] = 1; // 到达第0阶的方法数为1
        dp[1] = 1; // 到达第1阶的方法数为1

        // 循环计算每一阶的方法数
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2]; // 到达第i阶的方法数是第i-1阶和第i-2阶方法数之和
        }

        // 返回到达第n阶的方法数
        return dp[n];
    }
}

时间复杂度

时间复杂度为O(n)。这是因为我们需要从第2阶开始,一直计算到第n阶,每阶的计算时间是常数级别的,所以总的时间复杂度是线性的。

空间复杂度

空间复杂度为O(n)。这是因为我们使用了一个长度为n+1的数组来存储每一阶的方法数。如果n很大,这个数组会占用相当大的内存空间。

优化空间复杂度

实际上,我们并不需要存储所有的中间结果,只需要存储最后两阶的结果即可,因此可以进一步优化空间复杂度。

优化后的代码实现

class Solution {
    public int climbStairs(int n) {
        // 基本情况处理
        if (n <= 1) return 1;

        // 只需要存储最后两阶的结果
        int prev2 = 1; // 到达第1阶的方法数
        int prev1 = 1; // 到达第2阶的方法数
        int current = 0; // 当前阶的方法数

        // 循环计算每一阶的方法数
        for (int i = 3; i <= n; i++) {
            current = prev1 + prev2; // 到达第i阶的方法数是第i-1阶和第i-2阶方法数之和
            prev2 = prev1; // 更新第i-2阶的方法数
            prev1 = current; // 更新第i-1阶的方法数
        }

        // 返回到达第n阶的方法数
        return current;
    }
}

优化后的空间复杂度

优化后的空间复杂度为O(1)。这是因为我们只存储了最后两阶的结果,不依赖于n的大小,所以空间复杂度是常数级别的。


746. 使用最小花费爬楼梯

题目描述

给定一个整数数组cost,其中cost[i]表示从第i个台阶向上爬一个台阶或两个台阶的费用。可以选择从下标为0或1的台阶开始爬楼梯。目标是计算并返回到达楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20] 输出:15 解释:你将从下标为 1 的台阶开始。

  • 支付 15 ,向上爬两个台阶,到达楼梯顶部。 总花费为 15 。

示例 2:

输入:cost = [1,100,1,1,1,100,1,1,100,1] 输出:6 解释:你将从下标为 0 的台阶开始。

  • 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。

  • 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。

  • 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。

  • 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。

  • 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。

  • 支付 1 ,向上爬一个台阶,到达楼梯顶部。 总花费为 6 。

思路步骤

  1. 初始化动态规划数组:创建一个长度为cost.length + 1的数组dp,用于存储到达每个台阶的最低花费。

  2. 边界条件dp[0]dp[1]都初始化为0,因为从地面或第一个台阶开始爬不需要花费。

  3. 动态规划转移方程:从第2个台阶开始,使用循环计算每个台阶的最低花费。dp[i]的值是dp[i-1] + cost[i-1](从上一个台阶爬一个台阶上来的费用)和dp[i-2] + cost[i-2](从上上一个台阶爬两个台阶上来的费用)中的最小值。

  4. 返回结果:循环结束后,返回dp[length]作为结果,即到达楼梯顶部的最低花费。

代码实现

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int length = cost.length;
        // 初始化动态规划数组
        int[] dp = new int[length + 1];
        // 边界条件
        dp[0] = 0;
        dp[1] = 0;
        // 动态规划转移方程
        for (int i = 2; i <= length; i++) {
            // dp[i]是到达第i个台阶的最低花费
            dp[i] = Math.min(dp[i - 1] + cost[i - 1], // 从上一个台阶上来的费用
                             dp[i - 2] + cost[i - 2]); // 从上上一个台阶上来的费用
        }
        // 返回到达楼梯顶部的最低花费
        return dp[length];
    }
}

时间复杂度

时间复杂度为O(n),其中n是cost数组的长度。这是因为我们需要遍历整个数组一次,每次迭代的计算时间是常数级别的。

空间复杂度

空间复杂度为O(n),因为我们需要一个长度为n+1的数组dp来存储每个台阶的最低花费。

优化空间复杂度

实际上,我们并不需要存储所有的中间结果,只需要存储最后两个台阶的结果即可,因此可以进一步优化空间复杂度。

优化后的代码实现

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int length = cost.length;
        // 只需要存储最后两个台阶的结果
        int prev2 = 0; // 到达第0个台阶的最低花费
        int prev1 = 0; // 到达第1个台阶的最低花费
        int current = 0; // 当前台阶的最低花费

        // 动态规划转移方程
        for (int i = 2; i <= length; i++) {
            current = Math.min(prev1 + cost[i - 1], prev2 + cost[i - 2]);
            // 更新前两个台阶的结果
            prev2 = prev1;
            prev1 = current;
        }
        // 返回到达楼梯顶部的最低花费
        return current;
    }
}

优化后的空间复杂度

优化后的空间复杂度为O(1)。这是因为我们只存储了最后两个台阶的结果,不依赖于cost数组的大小,所以空间复杂度是常数级别的。

文章持续跟新,可以微信搜一搜公众号  rain雨雨编程 ],第一时间阅读,涉及数据分析,机器学习,Java编程,爬虫,实战项目等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值