leetcode 第198题 打家劫舍, 第213题 打家劫舍 II , 第337题 打家劫舍 III(Python解法)
Leetcode中最聪明的小偷—做程序员不如做小偷!
问题分析
198题 打家劫舍
第一题比较简单,具体如下:
首先该题可以看成是一个动态规划的问题,即从一个很小的范围开始求解,慢慢扩大到n个房子。由于不能偷连续的两家,所以其状态转移方程可以写为:dp[n] += max(dp[n-2], dp[n-3])(n >= 3),该方程是在房间数大于等于3的情况下展开的,所以n=0,1,2要单独考虑。最后返回的是倒数第一个和倒数第二个元素较大者。
此外,可以选择不开辟额外空间,直接在原nums数组上操作。
213题 打家劫舍 II
第二题是第一题的升级版,此题中所有的房子不再是一字排开,而是围城了一个圆环,与第一题不同之处在于,第一间房子和最后一间房子不能同时被盗。
该题下面有一个提示,就是将分两次遍历数组,由于第一个房子不能和最后一个房子同时被盗,那么两个数组分别为nums[:n-1]和nums[1:n]。接下来对这两个数组依次使用第一题的方法,返回两次的最大值。
需要注意的是,这里数组的长度n>=4, 在n=0,1,2,3时都要分别考虑。
337题 打家劫舍 III
第三题是最难的,此时的房子排列成了一个二叉树,同样还是不能同时盗窃两个相连的房子。这一题大致的思想与上面一样,都是使用动态规划的思想求出子树的最大值。
先从根节点开始不断地使用递归函数处理左右节点,递归函数返回值有两个,分别对应着两种情况。第一个数代表的是偷窃当前节点的房子所能取得的最大值,这个值等于当前节点值加上左右节点各自孩子的最大值(如果存在的话,不能与自己直接相连的子节点相加);另一个值是不偷当前节点的房子时所能取到的最大值(值为左节点与左节点孩子中较大者与右节点与右节点孩子中较大者相加求和)。叶子节点比较特殊,由于没有子节点,所以可以返回的第一个数是叶子节点值,第二个数为0。
在这里我们下面这个树做例子:
先使用递归函数一直到最左边的节点,那么叶子节点6, 7分别返回(6, 0), (7, 0)。递归函数回到节点4,此时4返回(4+0+0, max(6,0)+max(7,0)),即为(4,13)。这样一直不断返回,直到最后返回函数的结果为节点1的结果,从该结果中选取较大的作为整个函数的返回值。
源码
198题 打家劫舍`
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
if n == 0:
return 0
if n == 1:
return nums[0]
if n == 2:
return max(nums[0], nums[1])
nums[2] += nums[0]
for i in range(3, n):
nums[i] += max(nums[i-2], nums[i-3])
print(nums)
return max(nums[n-1], nums[n-2])
213题 打家劫舍 II
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
if n == 0:
return 0
if n == 1:
return nums[0]
if n == 2:
return max(nums[0], nums[1])
if n == 3:
return max(nums[0], nums[1], nums[2])
dp = [0] * n
dp[0],dp[1] = nums[0], nums[1]
dp[2] = nums[2] + nums[0]
for i in range(3, n-1):
dp[i] = nums[i] + max(dp[i-2], dp[i-3])
ret = max(dp[n-2], dp[n-3])
print("1=", dp)
dp[2] = nums[2]
dp[3] = nums[1] + nums[3]
for i in range(4, n):
dp[i] = nums[i] + max(dp[i-2], dp[i-3])
print("2=", dp)
return max(ret, dp[n-1], dp[n-2])
337题 打家劫舍 III
class Solution:
def rob(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if root:
return max(self.func(root))
return 0
def func(self, root):
lv = rv = (0, 0)
if root.left:
lv = self.func(root.left)
if root.right:
rv = self.func(root.right)
return root.val + lv[1] + rv[1], max(lv) + max(rv)