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

213. 打家劫舍 II

  • 213. 打家劫舍 II
    • 题目来源
    • 题目分析
      • 题目难度
      • 题目标签
      • 题目限制
    • 解题思路
      • 核心算法步骤
      • 代码实现
      • 代码解读
      • 性能分析
    • 测试用例
    • 扩展讨论
      • 优化写法
      • 其他实现
    • 总结

213. 打家劫舍 II

题目来源

213. 打家劫舍 II

题目分析

在这个问题中,房屋排列成一个圆形,小偷不能在同一晚上偷窃相邻的房屋,否则会触发警报。给定一个代表每个房屋存放金额的非负整数数组,我们需要计算小偷在不触动警报装置的情况下,能够偷窃到的最高金额。

题目难度

  • 难度:中等

题目标签

  • 标签:动态规划

题目限制

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

解题思路

这道题其实是198.打家劫舍添加了一个约束条件:首尾不能同时选,那么我们就可以分解问题为两个简单问题,让这个问题退化为198.打家劫舍类型
由于房屋排列成一个圆形,我们需要处理两个特殊情况:

  1. 偷窃从第一个房屋到倒数第二个房屋的情况(不偷第一个房屋)。
  2. 偷窃从第二个房屋到最后一个房屋的情况(不偷最后一个房屋)。

这两个子问题的解决方法与第198题198.打家劫舍相同,即使用动态规划。

核心算法步骤

  1. 分解问题

    • 第一个子问题:处理房屋从 nums[0]nums[n-2](即不包括最后一个房屋)。
    • 第二个子问题:处理房屋从 nums[1]nums[n-1](即不包括第一个房屋)。
  2. 动态规划

    • 对于每个子问题,使用动态规划来计算每种情况的最大偷窃金额,方法与第198题198.打家劫舍相同。

代码实现

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

/**
 * 213. 打家劫舍 II
 * @param nums 房屋存放金额的非负整数数组
 * @return 偷窃到的最高金额
 */
public static int rob2(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    if (nums.length == 1) {
        return nums[0];
    }
    if (nums.length == 2) {
        return Math.max(nums[0], nums[1]);
    }
    return Math.max(rob1(nums, 0, nums.length - 2), rob1(nums, 1, nums.length - 1));
}

/**
 * 动态规划求解单排房屋偷窃问题
 * @param nums 房屋存放金额的非负整数数组
 * @param start 起始房屋索引
 * @param end 结束房屋索引
 * @return 偷窃到的最高金额
 */
private static int rob1(int[] nums, int start, int end) {
    int dp0 = nums[start], dp1 = Math.max(nums[start], nums[start + 1]);
    for (int i = start + 2; i <= end; i++) {
        int dp2 = Math.max(dp1, dp0 + nums[i]);
        dp0 = dp1;
        dp1 = dp2;
    }
    return dp1;
}

代码解读

  • rob2 方法

    • 检查房屋数量的边界情况。
    • 调用 rob1 方法处理两个子问题,选择其中的最大值作为最终结果。
  • rob1 方法

    • 这是处理线性房屋排列问题的动态规划实现。
    • 使用 dp0dp1 来跟踪前两个状态的最高金额,通过循环遍历更新状态。

性能分析

  • 时间复杂度O(n),遍历房屋数组两次(分别计算两个子问题)。
  • 空间复杂度O(1),只使用了常数级别的额外空间。

测试用例

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

int[] nums1 = {2, 3, 2};
int result1 = rob2(nums1);
System.out.println(result1);
// 输出: 3 (偷窃 3)

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

扩展讨论

优化写法

  • 空间优化
    • rob1 方法中的空间使用已经是最优的,通过仅使用两个变量来跟踪状态。

其他实现

  • 递归
    • 除了动态规划,还可以使用递归方法,但会有较高的时间复杂度和空间复杂度,适用于较小的数据集。

总结

这道题目通过将圆形房屋问题转化为两个线性房屋问题来解决。动态规划的核心在于状态的转移和边界条件的处理,这种方法在处理类似问题时非常高效。


你可能感兴趣的:(LeetCode,算法题,leetcode,动态规划,java,算法)