代码改变世界

动态规划详细题解——力扣198.打家劫舍 - 实践

2026-01-28 14:25  tlnshuju  阅读(4)  评论(0)    收藏  举报

力扣198.打家劫舍

在这里插入图片描述


【LeetCode 198】打家劫舍(Java 动态规划详细题解)

一、题目描述

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金。

唯一的限制是:

相邻的两间房屋装有连通的防盗系统,如果在同一晚上闯入相邻的两间房屋,系统会自动报警。

给定一个数组 nums,表示每个房屋的金额,计算在不触动警报的情况下,最多能偷取的金额。


二、示例

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 偷 1 号房屋 (金额 = 1),再偷 3 号房屋 (金额 = 3),总金额 = 4。

示例 2:

输入: [2,7,9,3,1]
输出: 12
解释: 偷 1、3、5 号房屋,金额 = 2 + 9 + 1 = 12。

三、思路分析

本题是一个典型的动态规划问题
思考过程如下:

设:

  • dp[i] 表示偷到第 i 间房屋时能获得的最大金额

对于第 i 间房,有两种选择:

  1. 不偷第 i 间房
    那么最大金额就是前一间的最大值,即 dp[i-1]
  2. 偷第 i 间房
    因为相邻房不能同时偷,所以上一个能偷的是 i-2,此时金额为 dp[i-2] + nums[i]

因此,状态转移方程为:

dp[i] = max(dp[i-1], dp[i-2] + nums[i])

初始状态:

dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])

四、代码实现(Java)

class Solution {
public int rob(int[] nums) {
int n = nums.length;
if (n == 0) return 0;
if (n == 1) return nums[0];
// 初始化前两个状态
int[] dp = new int[n];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
// 动态规划迭代
for (int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[n - 1];
}
}

在这里插入图片描述


五、空间优化(O(1) 实现)

其实我们不需要整个数组,只需要保存 dp[i-1]dp[i-2]
因此可以进一步优化:

class Solution {
public int rob(int[] nums) {
int n = nums.length;
if (n == 0) return 0;
if (n == 1) return nums[0];
int prev2 = nums[0];                  // dp[i-2]
int prev1 = Math.max(nums[0], nums[1]); // dp[i-1]
for (int i = 2; i < n; i++) {
int current = Math.max(prev1, prev2 + nums[i]);
prev2 = prev1;
prev1 = current;
}
return prev1;
}
}

在这里插入图片描述


六、复杂度分析

项目分析
时间复杂度O(n),只需一次遍历数组
空间复杂度O(1),使用常数个变量

七、举例推导

nums = [2,7,9,3,1] 为例:

房屋编号金额计算过程dp 值
02dp[0]=22
17max(2,7)=77
29max(7,2+9)=1111
33max(11,7+3)=1111
41max(11,11+1)=1212

最终结果:dp[4] = 12


八、扩展思考

本题是打家劫舍系列的第一题。后续还可以学习:

题号名称特点
198打家劫舍直线排列
213打家劫舍 II房屋围成环
337打家劫舍 III房屋为二叉树结构

掌握这题后,理解后续题型会更加容易。


九、总结

  • 关键在于理解“不能偷相邻房屋” → 状态转移方程;
  • 动态规划是最优子结构与重叠子问题的结合;
  • 空间优化技巧常用于此类连续依赖的 DP 问题。