566 字
3 分钟
代码随想录算法训练营第三十四天 | 动态规划 Part07

198. 打家劫舍#

LeetCode 198. House Robber

思路分析:#

  • 使用递归 + 记忆化搜索(自顶向下)
  • dp[i] 表示从第 i 间房子开始偷窃所能获得的最大金额

Python代码:#

class Solution:
def rob(self, nums: List[int]) -> int:
# 初始化记忆数组,避免重复计算
self.memo = [-1] * len(nums)
return self.dp(nums, 0)
def dp(self, nums, start):
# 递归终止条件:超出数组范围
if start >= len(nums):
return 0
# 如果已经计算过,直接返回记忆值
if self.memo[start] != -1:
return self.memo[start]
# 决策:偷 or 不偷当前房子
res = max(
self.dp(nums, start + 1), # 不偷当前房子
nums[start] + self.dp(nums, start + 2) # 偷当前房子,然后跳过下一个
)
self.memo[start] = res
return res

213. 打家劫舍 II#

LeetCode 213. House Robber II

  • 环形问题 ⇒ 不能同时选择第一个和最后一个

  • 转化为两个线性子问题:

    • 情况一:不偷最后一个房子(范围:[0, n-2]
    • 情况二:不偷第一个房子(范围:[1, n-1]
  • 两者取最大值即可。

class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
# 分两种情况:不包含最后一个 和 不包含第一个
memo1 = [-1] * n
memo2 = [-1] * n
return max(
self.dp(nums, 0, n - 2, memo1), # 偷[0, n-2]
self.dp(nums, 1, n - 1, memo2) # 偷[1, n-1]
)
def dp(self, nums, start, end, memo):
# 边界条件
if start > end:
return 0
# 返回记忆值
if memo[start] != -1:
return memo[start]
# 决策:偷 or 不偷当前房子
res = max(
self.dp(nums, start + 1, end, memo), # 不偷当前房子
nums[start] + self.dp(nums, start + 2, end, memo) # 偷当前房子
)
memo[start] = res
return res

337. 打家劫舍 III#

LeetCode 337. House Robber III

  • 对每个节点做决策:偷 or 不偷
  • 返回值为一个二元组 (不偷该节点的最大值, 偷该节点的最大值)
  • 采用后序遍历递归 + 动态规划(树形DP)
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
# 返回值是一个元组:(不偷当前节点, 偷当前节点)
return max(self.dfs(root))
def dfs(self, node):
# 空节点返回 (0, 0)
if not node:
return (0, 0)
# 后序遍历左右子树
left = self.dfs(node.left)
right = self.dfs(node.right)
# 不偷当前节点:左右子节点可偷可不偷,取最大
not_rob = max(left) + max(right)
# 偷当前节点:不能偷左右子节点
rob = node.val + left[0] + right[0]
return (not_rob, rob)
代码随想录算法训练营第三十四天 | 动态规划 Part07
https://fuwari.vercel.app/posts/code_musings_day_thirty-four/
作者
Jarrett
发布于
2025-07-19
许可协议
CC BY-NC-SA 4.0