LeetCode 198.打家劫舍 动态规划详细解法

198. 打家劫舍

题目来源

198. 打家劫舍

题目分析

在这个问题中,小偷需要在一条街上偷窃房屋,而不能连续偷窃相邻的房屋,否则会触发警报。给定一个代表每个房屋存放金额的非负整数数组,计算小偷能够在一夜之内偷窃到的最高金额。

题目难度

  • 难度:简单

题目标签

  • 标签:动态规划

题目限制

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 400

解题思路

为了解决这个问题,我们可以使用动态规划。动态规划的思想是通过定义一个 dp 数组,记录在偷窃前 i 间房屋时能够获取到的最高金额。我们可以逐步计算,最终得到偷窃整个街道的最大收益。

核心算法步骤

  • 动态规划
    1. 定义 dp[i] 表示偷窃前 i 间房屋时,能获取的最高金额。
    2. 状态转移方程
      • dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i])
      • 即第 i 间房屋的最高金额为两种情况中的最大值:
        • 不偷第 i 间房屋时的最高金额 dp[i - 1]
        • 偷第 i 间房屋时的最高金额 dp[i - 2] + nums[i]
    3. 初始化边界条件:
      • dp[0] = nums[0]
      • dp[1] = Math.max(nums[0], nums[1])
    4. 最终结果为 dp[nums.length - 1],表示偷窃所有房屋时的最高金额。

代码实现

以下是求解房屋偷窃问题的 Java 代码:

/**
 * 198. 打家劫舍
 * @param nums 房屋存放金额的非负整数数组
 * @return 偷窃到的最高金额
 */

public static int rob(int[] nums) {
    int[] dp = new int[nums.length + 2];
    for (int i = 0; i < nums.length; i++) {
        dp[i + 2] = Math.max(dp[i + 1], dp[i] + nums[i]);
    }
    return dp[nums.length + 1];
}
//可读性更高的版本
public static int rob1(int[] nums) {
    if (nums.length == 0){
        return 0;
    }
    if (nums.length == 1){
        return nums[0];
    }
    int[] dp = new int[nums.length];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0], nums[1]);
    for (int i=2;i<nums.length;i++){
        dp[i]=Math.max(dp[i-1], dp[i-2]+nums[i]);
    }
    return dp[nums.length-1];
}

代码解读

  • 动态规划逻辑

    • dp[i + 2] = Math.max(dp[i + 1], dp[i] + nums[i]) 表示选择偷窃当前房屋 i 与不偷的最大值,并且下标从 i + 2 开始是为了便于处理状态转移的边界条件。
    • 通过 dp[i + 2] 的定义,可以简化边界条件,将其融入循环中,相当于将dp数组相比原来全部向后移了2位。
  • 路径管理

    • 使用一个 dp 数组来记录每个房屋位置的最高金额,最后返回 dp[nums.length + 1],即整个街道的最大偷窃金额。

性能分析

  • 时间复杂度O(n),遍历数组一次,每个房屋只计算一次最大金额。
  • 空间复杂度O(n),由于使用了一个 dp 数组来存储每个房屋位置的最高金额。

测试用例

你可以使用以下测试用例来验证代码的正确性:

int[] nums = {2, 7, 9, 3, 1};
int result = rob(nums);
System.out.println(result);
// 输出: 12 (偷窃 2 + 9 + 1)

int[] nums2 = {1, 2, 3, 1};
int result2 = rob(nums2);
System.out.println(result2);
// 输出: 4 (偷窃 1 + 3)

扩展讨论

优化写法

在空间复杂度上,可以进一步优化为 O(1),通过两个变量来记录当前状态和前一状态,从而省去 dp 数组的空间。

public int rob(int[] nums) {
        if (nums.length == 0){
            return 0;
        }
        if (nums.length == 1){
            return nums[0];
        }

        int dp0 = nums[0];
        int dp1 = Math.max(nums[0], nums[1]);
        for (int i=2;i<nums.length;i++){
            int dp2=Math.max(dp1, dp0+nums[i]);
            dp0=dp1;
            dp1=dp2;
        }
        return dp1;
    }

其他实现

除了动态规划,还可以通过递归加记忆化搜索的方式来解决这个问题,不过由于记忆化的重复计算,性能上不如动态规划高效。

总结

这道题目通过动态规划的方式解决了房屋偷窃问题。动态规划的核心在于状态的转移和边界条件的处理。这种方式在类似的数组问题中有广泛的应用,是理解动态规划思想的重要题目之一。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值