Leetcode213&&337 打家劫舍II&&III

155 阅读2分钟

数组复制可以用Arrays.copyOfRange(arr,start,end), 指定数组区间
此外,这个问题是将环状问题约化为两个单排列问题。

  • 状态定义:
    设动态规划列表 dp ,dp[i] 代表前 i 个房子在满足条件下的能偷窃到的最高金额。

  • 转移方程:
    dp[n+1] = max(dp[n],dp[n-1]+num)
    此处既然dp[n+1]只和前两个状态dp[n-1]和dp[n]有关,所以可以用常数级变量优化空间复杂度
    即:
    今天偷钱后最大值 = 昨天没偷钱最大值+今天钱数
    今天没偷钱最大值 = Math.max(昨天偷钱后最大值,昨天没偷钱后最大值)
    最后取两者较大值返回Math.max(今天偷钱后最大值,今天没偷钱最大值)

复杂度分析:
时间复杂度 O(N) : 两次遍历 nums 需要线性时间;
空间复杂度 O(1) : curRob和 curNoRob 使用常数大小的额外空间。

image.png

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

    public int robHouse(int[] nums){
        int curRob=0,curNoRob=0,preRob;
        for(int num:nums){
            preRob = curRob;
            curRob = curNoRob+num;
            curNoRob = Math.max(preRob,curNoRob);
        }
        return Math.max(curRob,curNoRob);
    }
}

下面这个问题,是对上一个问题的升级

image.png

方法一是在递归的基础上,增加了记忆集来提升查询速度,没想到竟然超时了。。。
方法二是直接动态规划:
定义数组 int[] res = new int[2] ,其中0 代表不偷,1 代表偷。任何一个节点能偷到的最大钱的状态可以定义为:

  • 当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
  • 当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数

参考

class Solution {
    //方案一:递归+记忆集
    public int rob(TreeNode root) {
        if(root==null) return 0;
        return recurRob(root,new HashMap<TreeNode,Integer>());
    }

    public int recurRob(TreeNode root,HashMap<TreeNode,Integer> maxMoney){
        if(root==null) return 0;
        if(maxMoney.containsKey(root)) return maxMoney.get(root);
        int money = root.val;
        if(root.left!=null){
            money+=rob(root.left.left) + rob(root.left.right);
        }
        if(root.right!=null){
            money+=rob(root.right.left) + rob(root.right.right);
        }
        int max = Math.max(money,rob(root.left)+rob(root.right));
        maxMoney.put(root,max);
        return max;
    }

    //方案二:
    public int rob(TreeNode root){
        int[] res = recurRob(root);
        return Math.max(res[0],res[1]);
    }

    public int[] recurRob(TreeNode root){
        if(root==null) return new int[2];
        int[] res = new int[2];
        int[] left = recurRob(root.left);
        int[] right = recurRob(root.right);
        res[0] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
        res[1] = left[0]+right[0]+root.val;
        return res;
    }
}