198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

1
2
3
4
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

1
2
3
4
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

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

题解:

动态规划

首先考虑最简单的情况。

  • 只有一间房的情况,只能偷这间房。
  • 只有两间房的情况,盗窃两间房中金额较高的房。

然后考虑房间大于两间房的情况:

  • 对于第i间房,如果选择偷窃,那么就不能偷窃第i-1间房,偷窃的金额就为前i-2间房偷窃的总金额加上当前房间的金额。
  • 对于第i间房,如果选择不偷窃,那么偷窃的总金额即为前i-1间房偷窃的总金额。

在以上两种情况下选取最大值进行偷窃,则可得到状态转移方程:

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

边界条件为:

1
2
3
//边界条件
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);

最终结果为dp[length - 1],length是数组的长度。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public int rob(int[] nums) {
/*
* 如果选择偷第i家,则证明第i-2家为最优解,加上当前i即可
* 如果不选择偷第i家,则证明第i-1家为最优解
*
* 即 dp[i] = Math.max(dp[i-1],dp[i -2 ] + nums[i])
*
* 边界条件:
* dp[0] = nums[0],只有一间房,只能偷这间房
* dp[1] = Math.max(nums[0],nums[1]) 两间房,偷钱多的哪一家
*
*
*
* */
if (nums == null || nums.length == 0) {
return 0;
}

int length = nums.length;


if (length == 1) {
return nums[0];
}


//辅助dp数组
int[] dp = new int[length];

//边界条件
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);

for (int i = 0; i < length; i++) {
//递推公式
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}

return dp[length - 1];


}
  • 时间复杂度:O(N),需要遍历数组。
  • 空间复杂度:O(N),需要一个大小为数组长度的辅助dp数组。
空间优化:

每间房的最高总金额只和前两件房屋的最高总金额有关,所以可以只用两个值来时刻存储前两件房屋的最高金额即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public int rob(int[] nums) {

if (nums == null || nums.length == 0) {
return 0;
}

int length = nums.length;


if (length == 1) {
return nums[0];
}

//边界条件
//记录前nums[i - 1]的最大金额
int first = nums[0];
//记录前nums[i-2]的最大金额
int second = Math.max(nums[0], nums[1]);

for (int i = 2; i < length; i++) {
//防止数据丢失
int temp = second;
//状态转移方程
second = Math.max(second, first + nums[i]);
first = second;
}
return second;


}
  • 时间复杂度:O(N),需要遍历数组。
  • 空间复杂度:O(1),只使用了两个额外变量来存储前两间房屋的最高金额。