Skip to content

打家劫舍系列

打家劫舍状态转移

LC 198. 打家劫舍

不能偷相邻的房屋,求能偷到的最大金额。

dp[i] = 前 i 间房能偷到的最大金额。

javascript
function rob(nums) {
  const n = nums.length;
  if (n === 1) return nums[0];
  let prev2 = 0, prev1 = 0;
  for (const num of nums) {
    const curr = Math.max(prev1, prev2 + num);
    prev2 = prev1;
    prev1 = curr;
  }
  return prev1;
}

状态转移:dp[i] = max(dp[i-1], dp[i-2] + nums[i])(偷或不偷第 i 间)

LC 213. 打家劫舍 II(环形)

房屋首尾相连形成环,第一间和最后一间不能同时偷。

技巧:拆成两个子问题——不偷第一间 or 不偷最后一间,取最大值。

javascript
function rob(nums) {
  const n = nums.length;
  if (n === 1) return nums[0];

  function robRange(lo, hi) {
    let prev2 = 0, prev1 = 0;
    for (let i = lo; i <= hi; i++) {
      const curr = Math.max(prev1, prev2 + nums[i]);
      prev2 = prev1;
      prev1 = curr;
    }
    return prev1;
  }

  return Math.max(robRange(0, n - 2), robRange(1, n - 1));
}

LC 337. 打家劫舍 III(树形 DP)

房屋排列成二叉树,不能偷直接相连的节点。

每个节点返回两个值:[不偷该节点的最大值, 偷该节点的最大值]

javascript
function rob(root) {
  function dfs(node) {
    if (!node) return [0, 0]; // [不偷, 偷]

    const left = dfs(node.left);
    const right = dfs(node.right);

    // 不偷当前节点:子节点可偷可不偷,取最大值
    const notRob = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
    // 偷当前节点:子节点不能偷
    const doRob = node.val + left[0] + right[0];

    return [notRob, doRob];
  }

  const result = dfs(root);
  return Math.max(result[0], result[1]);
}

三道题的递进关系

题目结构DP 类型
LC 198线性数组一维 DP
LC 213环形数组分两段一维 DP
LC 337二叉树树形 DP(后序遍历)

复杂度

  • LC 198/213:时间 O(n),空间 O(1)
  • LC 337:时间 O(n),空间 O(h)

LeetCode 练习

  • LC 198. 打家劫舍 ⭐
  • LC 213. 打家劫舍 II
  • LC 337. 打家劫舍 III ⭐

面试算法可视化图解