LeetCode之一维动态规划

70. 爬楼梯

class Solution {

    // 思路:基于动态规划
    public int climbStairs(int n) {
        // 处理边界条件,如果台阶数为 1,只有一种方法
        if (n == 1) {
            return 1;
        }
        // 用数组记录 n 个台阶的方法数,其中跳过了索引 0
        int[] result = new int[n + 1];
        // 台阶数为 1 是 1 种方法
        result[1] = 1;
        // 台阶数为 2 是 2 种方法
        result[2] = 2;
        // 从台阶数为 3 开始遍历到最后
        for (int i = 3; i <= n; i++) {
            // 状态转移方程:到达 i 阶台阶的方法数等于到达 i - 1 阶台阶的方法数加上到达 i - 2 阶台阶的方法数
            result[i] = result[i - 1] + result[i - 2];
        }
        // 最后返回 n 阶台阶的方法数即是结果
        return result[n];
    }

}

198. 打家劫舍

class Solution {
    public int rob(int[] nums) {
        // 1. 检查输入是否为空或长度为 0
        if (nums == null || nums.length == 0) {
            return 0; // 如果是空数组,返回 0
        }
        
        // 2. 如果只有一个房屋,直接返回那个房屋的值
        if (nums.length == 1) {
            return nums[0]; // 只有一个选择,只能偷这一家
        }
        
        // 3. 如果有两个房屋,返回这两个房屋中较大的值
        if (nums.length == 2) {
            return Math.max(nums[0], nums[1]); // 选择最大值
        }
        
        // 4. 创建 dp 数组,用于存储到每个房屋的最大金额
        int[] dp = new int[nums.length];
        
        // 5. 初始化第一个房屋的最大值为其自身的值
        dp[0] = nums[0];
        
        // 6. 初始化第二个房屋的最大值为前两个房屋的较大值
        dp[1] = Math.max(nums[0], nums[1]);
        
        // 7. 从第三个房屋开始进行动态规划计算
        for (int i = 2; i < nums.length; i++) {
            // 8. dp[i] 表示偷取到第 i 个房屋的最大金额
            //   - 可以选择偷当前房屋 i,加上之前可以偷的最大金额 (dp[i - 2])
            //   - 或者不偷当前房屋,继承之前的最大金额 (dp[i - 1])
            dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        
        // 9. 返回到最后一个房屋的最大偷窃金额
        return dp[nums.length - 1];
    }
}

139. 单词拆分

import java.util.HashSet; // 导入 HashSet 类
import java.util.List; // 导入 List 接口
import java.util.Set; // 导入 Set 接口

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        // 1. 将单词字典转换为 HashSet,以便快速查找
        Set<String> wordSet = new HashSet<>(wordDict);
        
        // 2. 创建一个 dp 数组,dp[i] 表示 s 的前 i 个字符是否可以拆分为字典中的单词
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true; // 3. 空字符串可以被拆分,初始化 dp[0] 为 true
        
        // 4. 遍历 dp 数组,从 dp[1] 到 dp[s.length()]
        for (int i = 1; i < dp.length; i++) {
            // 5. 遍历从 0 到 i 的所有可能的 j
            for (int j = 0; j < i; j++) {
                // 6. 如果 dp[j] 为 true,且 s 的子串 s[j:i] 在字典中
                if (dp[j] && wordSet.contains(s.substring(j, i))) {
                    dp[i] = true; // 7. 更新 dp[i] 为 true
                    break; // 8. 找到拆分方式,跳出内层循环
                }
            }
        }
        
        // 9. 返回最终结果,判断整个字符串 s 是否可以被拆分
        return dp[s.length()];
    }
}

322. 零钱兑换

class Solution {
    public int coinChange(int[] coins, int amount) {
        // 定义最大值,这里的 amount + 1 作为一个超出可能结果范围的值,用于后续判断无法兑换的情况
        int max = amount + 1;
        // 定义金额动态数组 dp,存储每个金额所需要的最小硬币数量
        int[] dp = new int[amount + 1];
        // 将 dp 数组初始化为一个根本无法达到的大值,如果后续判断不满足,说明找不开
        Arrays.fill(dp, max);
        // 边界条件,金额为 0 时不需要任何硬币就能兑换,所以 dp[0] = 0
        dp[0] = 0;
        // 数额为 amount,从 1 开始算各个金额所需要的最新面值
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                // 需要兑换的金额必须得大于等于现有面值吧
                if (i >= coins[j]) {
                    // dp[i] 表示兑换金额为 i 时所需的最小硬币数量,dp[i - coins[j]] + 1 表示使用当前面值 coins[j] 时的可能最小硬币数量
                    // Math.min 取两者中的较小值更新 dp[i]
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        // 如果金额为 max(amount + 1)说明没兑换出去,返回 -1,否则就是 dp[amount],即兑换金额为 amount 时所需的最小硬币数量
        return dp[amount] > amount? -1 : dp[amount];
    }
}

300. 最长递增子序列

class Solution {
    // 接受一个整数数组 nums 作为输入,返回数组中最长递增子序列的长度
    public int lengthOfLIS(int[] nums) {
        // 如果输入数组 nums 的长度为 0,则返回 0,因为空数组没有递增子序列
        if (nums.length == 0) {
            return 0;
        }
        // 创建一个整数数组 dp,长度与 nums 相同。dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度
        int[] dp = new int[nums.length];
        // 将 dp[0] 设置为 1,因为以第一个元素结尾的最长递增子序列至少为 1(它本身)
        dp[0] = 1;
        // 创建一个变量 maxans,用于记录迄今为止找到的最长递增子序列的长度,同时初始化为 1
        int maxans = 1;
        // 外层循环遍历数组,从第二个元素开始,遍历数组 nums 的每个元素
        for (int i = 0; i < nums.length; i++) {
            // 在内层循环之前,将 dp[i] 设置为 1,表示以 nums[i] 结尾的子序列至少包含它本身
            dp[i] = 1;
            // 遍历从 0 到 i - 1 的每个元素 j,判断当前元素 nums[i] 是否大于 nums[j]
            for (int j = 0; j < i; j++) {
                // 如果 nums[i] 大于 nums[j],那么 nums[i] 可以添加到以 nums[j] 结束的递增子序列中
                if (nums[i] > nums[j]) {
                    // 更新 dp[i] 为 dp[j] + 1 和当前 dp[i] 中的最大值,这样可以找到以 nums[i] 结尾的最长递增子序列的长度
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            // 在内层循环结束后,更新 maxans,记录迄今为止的最长递增子序列的长度。
            maxans = Math.max(dp[i], maxans);
        }
        // 返回 maxans,表示找到的最长递增子序列的长度
        return maxans;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值