LeetCode 213:打家劫舍 II(House Robber II)------ 题解 ✅
📖 内容概要
你是一个专业的小偷,计划偷窃沿街的房屋。
所有房屋围成一圈 ,这意味着第一间房和最后一间房相邻。
如果两间相邻的房屋在同一晚上被闯入,系统会自动报警。
求在不触动警报的前提下,一夜之间能够偷窃到的最高金额。
✅ 动态规划
✅ 环状结构拆环
✅ 基于 LeetCode 198 的变形
💡 解题思路(核心)
一、关键难点:环形限制
由于房屋是 环形:
- 偷了第 1 间 → 不能偷第 n 间
- 偷了第 n 间 → 不能偷第 1 间
二、拆环为链(核心思想)
将环形问题拆分为两个线性问题:
| 情况 | 偷窃范围 |
|---|---|
| 情况 1 | 第 1 间 ~ 第 n-1 间 |
| 情况 2 | 第 2 间 ~ 第 n 间 |
✅ 两种情况互不冲突
✅ 分别用「打家劫舍 I」求解
✅ 取最大值
三、状态转移(复用 198)
java
dp[i] = max(
dp[i - 1], // 不偷当前
dp[i - 2] + nums[i] // 偷当前
)
✅ AC 代码(Java)
java
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if (len == 1) {
return nums[0];
}
// 拆成两个线性问题
int[] a = Arrays.copyOf(nums, len - 1); // 不含最后一间
int[] b = Arrays.copyOfRange(nums, 1, len); // 不含第一间
return Math.max(help(a), help(b));
}
// 线性打家劫舍(198 题)
public int help(int[] nums) {
int len = nums.length;
if (len == 1) {
return nums[0];
}
int[] dp = new int[len];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < len; i++) {
dp[i] = Math.max(
dp[i - 1],
dp[i - 2] + nums[i]
);
}
return dp[len - 1];
}
}
⏱️ 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n)(可优化为 O(1)) |
🔍 与「打家劫舍 I」的对比
| 题目 | 结构 | 解法 |
|---|---|---|
| 198 | 线性街道 | 一次 DP |
| 213 | 环形 | 拆成两个线性 DP |
✅ 一句话总结
环形 = 去掉第一家 or 去掉最后一家,取两者最大值。
📌 面试加分点(建议记住)
- ✅ 为什么不能直接用线性 DP?
- ✅ 为什么拆成两段就一定正确?
- ✅ 如何进一步优化空间?
- ✅ 与树形 DP(337)的区别