198.打家劫舍

思路
- dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。
- 对于第i个房间偷还是不偷?
如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。
如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(注意这里是考虑,并不是一定要偷i-1房)
然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]) - 初始化
dp[0] = nums[0] dp[1] = max(nums[0], nums[1]) - 遍历顺序从前往后
代码
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) <= 2:
return max(nums)
dp = [0] * len(nums)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(dp[i-2] + nums[i], dp[i-1])
return dp[-1]
213.打家劫舍II

思路
这道题是打家劫舍i,围成圈的版本。也就是说打劫了第一个屋子就不能打劫最后一个屋子。
所以这道题可以看成是打家劫舍i作用于nums[:-1]和nums[1:]上,取最大值。
代码
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) <= 2:
return max(nums)
def rob_helper(nums):
dp = [0] * len(nums)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
return dp[-1]
return max(rob_helper(nums[:-1]), rob_helper(nums[1:]))
337.打家劫舍III

思路
后序遍历。每个节点可以偷也可以不偷。
rob, not_rob 分别记录了偷与不偷能获得的最大金钱。
代码
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
def dfs(node):
if node is None:
return 0, 0
l_rob, l_not_rob = dfs(node.left)
r_rob, r_not_rob = dfs(node.right)
rob = l_not_rob + r_not_rob + node.val
not_rob = max(l_rob, l_not_rob) + max(r_not_rob, r_rob)
return rob, not_rob
rob, not_rob = dfs(root)
return max(rob, not_rob)
- 时间复杂度:
O(n),每个节点只遍历了一次 - 空间复杂度:
O(log n),算上递推系统栈的空间
本文分析了LeetCode中的三个打家劫舍问题:基础版(动态规划),环形版本(动态规划应用),以及二叉树版本(后序遍历)。详细讲解了对应的解题思路和代码实现。
640

被折叠的 条评论
为什么被折叠?



