LeetCode ---- 打家劫舍系列问题思路与题解

Posted TheWhc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode ---- 打家劫舍系列问题思路与题解相关的知识,希望对你有一定的参考价值。

打家劫舍

198. 打家劫舍

/**
 *  思路: 动态规划
 *  1. 确定dp数组以及下标含义
 *     dp[i]: 考虑下标i(包括i)以内的房屋,最多可以偷窃的金额dp[i]
 *
 *  2. 确定递推公式
 *     dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
 *              不偷第i号房屋    偷第i号房屋
 *  3. 初始化
 *     dp[0] = nums[0]
 *      dp[1] = Math.max(nums[0], nums[1])
 *
 *  4. 确定遍历顺序
 *     从前往后遍历
 *
 *  5. 举例推导dp数组
 *          2  7  9  3  1
 * dp数组   2  7  11 11 12
    *  时间: O(n)
 *  空间: O(n)
 */
public int rob(int[] nums) 
   if(nums == null || nums.length == 0) 
      return 0;
   

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

   int[] dp = new int[nums.length];

   // dp[0]一定是1号房屋的值
   dp[0] = nums[0];
   // 取决于1号和2号房屋的最大值
   dp[1] = Math.max(nums[0], nums[1]);

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

   return dp[nums.length-1];

213. 打家劫舍II

/**
 *  思路: 动态规划
 *
 *  成环的偷盗问题分为三种情况:
 *     1. 不考虑首尾元素偷盗
 *      2. 考虑首部元素, 不考虑尾部元素偷盗
 *      3. 考虑尾部元素, 不考虑首部元素偷盗
 *
 *      其中2和3情况已经包括了1, 所以1情况可以忽略
 *      所以最后结果取 Math.max(情况2,情况3)
 *
 *  时间: O(n)
 *  空间: O(n)
 */
public int rob(int[] nums) 
   if(nums == null || nums.length == 0) 
      return 0;
   

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

   return Math.max(robRange(nums, 0, nums.length-1), robRange(nums, 1, nums.length));


private int robRange(int[] nums, int start, int end) 
   int[] dp = new int[end - start];

   if(dp.length == 1) 
      return nums[start];
   

   dp[0] = nums[start];
   dp[1] = Math.max(nums[start], nums[start+1]);

   for (int i = 2; i < dp.length; i++) 
      dp[i] = Math.max(dp[i-1], dp[i-2] + nums[start + i]);
   

   return dp[dp.length-1];

337. 打家劫舍III

树形dp

// 题意: 如果偷取了当前节点,那么它的孩子节点都不能偷取; 如果不偷取当前节点,那么它的孩子节点都能偷取

/**
 *  思路: 树形dp
 *
 *  树形dp: 在树上进行递归公式的推导
 * 
 *  对一个节点偷与不偷得到的最大金额进行记录,然后才能用状态转移记录状态的变化
 *
 *  递归函数
 *  1. 确定递归函数的参数和返回值
 *     - 返回值为一个长度为2的数组,表示偷与不偷两个状态
 *     - int[] robTree(TreeNode cur)
 *
 *  2. 确定终止条件
 *     (空节点时,偷与不偷都是为0)
 *     - if(cur == null) 
 *        return new int[]0, 0;
 *     
 *
 *  3. 确定遍历顺序
 *     后序遍历(左右根)
 *     通过递归得到左节点,得到左节点偷与不偷的金额
 *      通过递归得到右节点,得到右节点偷与不偷的金额
 *      下标为0表示不偷, 下标为1表示偷
 *      int[] left = robTree(cur.left); // 左
 *      int[] right = robTree(cur.right); // 右
 *
 *  4. 确定单层递归的逻辑
 *     如果偷取当前节点,那么孩子节点都不能偷取, int val1 = cur.val + left[0] + right[0]
 *     如果不偷取当前节点,那么孩子节点都能偷取,偷不偷选一个最大的值, int val2 = Math.max(left[0],left[1]) + Math.max(right[0],right[1])
 *
 *  5. 举例推导
 *        3 6,7
 *      /  \\
 * 3,22   3 1,3
 *      \\    \\
 * 0,33    1 0,1
 *
 *  时间: O(n)
 *  空间: O(logn)
 */
public int rob(TreeNode root) 
   if(root == null) 
      return 0;
   
   int[] res = robTree(root);
   return Math.max(res[0], res[1]);


private int[] robTree(TreeNode cur) 
   // 递归终止条件
   if(cur == null) 
      return new int[]0, 0;
   

   // 后序遍历
   int[] left = robTree(cur.left); // 左
   int[] right = robTree(cur.right); // 右

   // 单层递归逻辑
   // 偷当前节点 + 左节点不偷 + 右节点不偷
   int val1 = cur.val + left[0] + right[0];
   // 不偷取当前节点 + 偷左节点中最大值 + 偷右节点最大值
   int val2 = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);

   return new int[]val2, val1;

递归 + 记忆化

/**
 *  思路: 递归 + 记忆化
 *
 *  实时计算每个节点偷与不偷的情况
 *
 *  时间: O(n)
 *  空间: O(logn)
 */
// 记忆化避免重复计算
Map<TreeNode, Integer> map = new HashMap<>();
public int rob(TreeNode root) 
   if(root == null) 
      return 0;
   

   if(root.left == null && root.right == null) 
      return root.val;
   

   if(map.containsKey(root)) 
      return map.get(root);
   

   // 偷取当前节点
   int val1 = root.val;
   if(root.left != null)
      val1 += rob(root.left.left) + rob(root.left.right);
   if(root.right != null)
       val1 += rob(root.right.left) + rob(root.right.right);

   // 不偷取当前节点
   int val2 = rob(root.left) + rob(root.right);

   // 记录当前结果
   map.put(root, Math.max(val1, val2));

   return Math.max(val1, val2);

总结

以上是关于LeetCode ---- 打家劫舍系列问题思路与题解的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode ---- 打家劫舍系列问题思路与题解

Leetcode:打家劫舍系列

LeetCode刷题笔记-动态规划-day3

LeetCode刷题笔记-动态规划-day3

LeetCode刷题笔记-动态规划-day3

算法刷题打卡041 | 动态规划9-打家劫舍系列