题目描述(中等难度)

213. House Robber II - 图1

198 题 House Robber 的延续。一个数组,每个元素代表商店的存款,一个小偷晚上去偷商店,问最多能偷多少钱。有一个前提,不能偷相邻的商店,不然警报会响起。这道题的不同之处在于,商店是环形分布,所以第一家和最后一家也算作相邻商店。

解法一

这道题和 198 题 的区别在题目描述中也指出来了,即偷了第一家就不能偷最后一家。

所以顺理成章,偷不偷第一家我们单独考虑一下即可。

偷第一家,也就是求出在前 n - 1 家中偷的最大收益,也就是不考虑最后一家的最大收益。

不偷第一家,也就是求第 2 家到最后一家中偷的最大收益,也就是不考虑第一家的最大收益。

然后只需要返回上边两个最大收益中的较大的即可。

图示的话就是下边的两种范围。

  1. X X X X X X
  2. ^ ^
  3. X X X X X X
  4. ^ ^

我们看一下之前求全部商店的最大收益的代码,把最后优化的代码直接贴过来了,大家可以到 198 题 看详细的。

  1. public int rob(int[] nums) {
  2. int n = nums.length;
  3. if (n == 0) {
  4. return 0;
  5. }
  6. if (n == 1) {
  7. return nums[0];
  8. }
  9. int pre = 0;
  10. int cur = nums[0];
  11. for (int i = 2; i <= n; i++) {
  12. int temp = cur;
  13. cur = Math.max(pre + nums[i - 1], cur);
  14. pre = temp;
  15. }
  16. return cur;
  17. }

为了适应这道题的算法,我们可以对上边的代码进行改造。增加所偷的商店的范围的参数。

  1. public int robHelper(int[] nums, int start, int end) {
  2. int n = nums.length;
  3. if (n == 0) {
  4. return 0;
  5. }
  6. if (n == 1) {
  7. return nums[0];
  8. }
  9. int pre = 0;
  10. int cur = nums[start];
  11. for (int i = start + 2; i <= end; i++) {
  12. int temp = cur;
  13. cur = Math.max(pre + nums[i - 1], cur);
  14. pre = temp;
  15. }
  16. return cur;
  17. }

有了上边的代码,这道题就非常好写了。

  1. public int rob(int[] nums) {
  2. //考虑第一家
  3. int max1 = robHelper(nums, 0, nums.length - 1);
  4. //不考虑第一家
  5. int max2 = robHelper(nums, 1, nums.length);
  6. return Math.max(max1, max2);
  7. }
  8. public int robHelper(int[] nums, int start, int end) {
  9. int n = nums.length;
  10. if (n == 0) {
  11. return 0;
  12. }
  13. if (n == 1) {
  14. return nums[0];
  15. }
  16. int pre = 0;
  17. int cur = nums[start];
  18. for (int i = start + 2; i <= end; i++) {
  19. int temp = cur;
  20. cur = Math.max(pre + nums[i - 1], cur);
  21. pre = temp;
  22. }
  23. return cur;
  24. }

这道题通过分类的思想,成功将新问题化解到了已求解问题上,这个思想也经常遇到。

windliang wechat

添加好友一起进步~

如果觉得有帮助的话,可以点击 这里 给一个 star 哦 ^^

如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 这里 查看详情