打家劫舍系列

这个系列的问题一共有三个,题目如下:

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

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

    • 示例 1:

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

    • 示例 2:

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

  2. 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

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

    • 示例 1:

      输入:nums = [2,3,2]
      输出:3
      解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

    • 示例 2:

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

    • 示例 3:

      输入:nums = [0]
      输出:0

  3. 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

    计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

    • 示例 1:

      输入: [3,2,3,null,3,null,1]

            3
           / \
          2   3
           \   \ 
            3   1
      

      输出: 7
      解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

    • 示例 2:

      输入: [3,4,5,1,3,null,1]

              3
             / \
            4   5
           / \   \ 
          1   3   1
      

      输出: 9
      解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

首先,这三道题明显都是动态规划的问题,三道题的思路如下:

  1. 第一题相对比较简单,我们新建一个dp数组,dp[i]的值就是dp[i - 2] + nums[i]和dp[i - 1]之间比较大的那个。
  2. 第二题是在第一题中变形来的,相对于第一题就是收尾相连形成了一个环,即第一家和最后一家不能同时偷,那么我们就不带第一家算出最大值,然后再不带最后一家算出最大值,这两个中比较大的值就是解。
  3. 第三个相对难一点,我们可以采取后序遍历,这样根节点都会在子节点之后遍历到,然后每一个节点都有两种情况,偷还是不偷,如果当前节点不偷,那么这个节点的最大值就是两个子节点分别取偷和不偷的最大值相加,如果当前节点偷了,根据不能连续偷的定义,只能去两个节点不偷相加,如此递归,即可求出答案。

代码

问题1:

class Solution {
    public int rob(int[] nums) {
        if (Objects.isNull(nums) || nums.length == 0) {
            return 0;
        } else if (nums.length == 1) {
            return nums[0];
        } else if (nums.length == 2) {
            return Math.max(nums[1], nums[0]);
        }
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[1], nums[0]);
        for (int i = 2; i < nums.length; i++) {
            dp[i] = Math.max(nums[i] + dp[i - 2], dp[i - 1]);
        }
        return dp[nums.length - 1];
    }
}

问题2:

class Solution {
    public int rob(int[] nums) {
        if (Objects.isNull(nums) || nums.length == 0) {
            return 0;
        } else if (nums.length == 1) {
            return nums[0];
        }
        return Math.max(helper(Arrays.copyOfRange(nums, 0, nums.length - 1)), helper(Arrays.copyOfRange(nums, 1, nums.length)));
    }

    private int helper(int[] nums) {
        if (Objects.isNull(nums) || nums.length == 0) {
            return 0;
        } else if (nums.length == 1) {
            return nums[0];
        } else if (nums.length == 2) {
            return Math.max(nums[1], nums[0]);
        }
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[1], nums[0]);
        for (int i = 2; i < nums.length; i++) {
            dp[i] = Math.max(nums[i] + dp[i - 2], dp[i - 1]);
        }
        return dp[nums.length - 1];
    }
}

问题3:

class Solution {
 
    public int rob(TreeNode root) {
        int[] res = helper(root);
        return Math.max(res[0], res[1]);
    }

    private int[] helper(TreeNode root) {
        if (Objects.isNull(root)) {
            return new int[2];
        }
        int[] l = helper(root.left);
        int[] r = helper(root.right);
        int[] res = new int[2];
        res[0] = root.val + l[1] + r[1];
        res[1] = Math.max(l[0], l[1]) + Math.max(r[0], r[1]);
        return res;
    }
}