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)
,算上递推系统栈的空间