Leetcode 337:打家劫舍 III(最詳細的解法!!!)
阿新 • • 發佈:2018-12-11
在上次打劫完一條街道之後和一圈房屋後,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,我們稱之為“根”。 除了“根”之外,每棟房子有且只有一個“父“房子與之相連。一番偵察之後,聰明的小偷意識到“這個地方的所有房屋的排列類似於一棵二叉樹”。 如果兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。
計算在不觸動警報的情況下,小偷一晚能夠盜取的最高金額。
示例 1:
輸入: [3,2,3,null,3,null,1] 3- / \ 2 3 \ \ 3- 1- 輸出: 7 解釋: 小偷一晚能夠盜取的最高金額 = 3 + 3 + 1 = 7.
示例 2:
輸入: [3,4,5,1,3,null,1]
3
/ \
4- 5-
/ \ \
1 3 1
輸出: 9
解釋: 小偷一晚能夠盜取的最高金額 = 4 + 5 = 9.
解題思路
思路和之前一樣,我們遍歷整棵二分搜尋樹,對於每個節點,我們都需要判斷這個節點的值我們需不需要取。例如
3<-
/ \
2 3
\ \
3 1
如果我們取3
,那麼3.left and 3.right
我們都不能取了;如果我們不取,那麼最大值來自於左右孩子的和,也就是3.left+3.right
,但是對於左右孩子來說又涉及到了上面的問題,我們是取還是不取呢?這樣迴圈遞推下去,知道節點為null
0
即可。那麼我們對於每個節點,都要設計一個結構來描述取和不取這樣的操作,使用list
即可,list[0]
表示取,而list[1]
表示不取。
我們定義root
的左右孩子為l & r
,左孩子l
的左右孩子為ll & lr
,右孩子r
的左右孩子為rl & rr
。那麼我們可以寫出這樣的方程
result = max(root.val + f(ll) + f(lr) + f(rl) + f(rr), f(l) + f(r))
f = max(rob, norob)
我們可以很快速的寫出下面程式碼
class Solution:
def rob(self, root) :
"""
:type root: TreeNode
:rtype: int
"""
check = self._rob(root)
return max(check[0], check[1])
def _rob(self, root):
if not root:
return [0, 0]
l, r = self._rob(root.left), self._rob(root.right)
return l[1] + r[1] + root.val, max(l) + max(r)
但是這個程式碼依舊有優化的空間,我們可以通過記憶化搜尋的方法繼續優化這個程式碼。但是我們怎麼記憶各個節點的訪問情況呢?之前的問題都是list
,所以我們可以通過index
來建立搜尋列表。對於這個問題我們有一個小技巧就是通過l & r
表示左右,例如lll
表示左子樹的左子樹的左子樹。
class Solution:
def rob(self, root):
"""
:type root: TreeNode
:rtype: int
"""
mem = dict()
check = self._rob(root, "", mem)
return max(check[0], check[1])
def _rob(self, root, path, mem):
if not root:
return [0, 0]
if path in mem:
return mem[path]
l, r = self._rob(root.left, path + 'l', mem), self._rob(root.right, path + 'r', mem)
mem[path] = l[1] + r[1] + root.val, max(l) + max(r)
return mem[path]
我們怎麼通過迭代解決這個問題呢?實際上這個問題變成了我們怎麼去遍歷一顆二叉樹,怎麼做呢?
我這裡通過前序遍歷解決這個問題
class Solution:
def rob(self, root):
"""
:type root: TreeNode
:rtype: int
"""
stack = [(0, root)]
result = {None: (0, 0)}
while stack:
rob, node = stack.pop()
if not node:
continue
if not rob:
stack.extend([(1, node), (0, node.right), (0, node.left)])
else:
result[node] = (result[node.left][1] + result[node.right][1] + node.val,\
max(result[node.left]) + max(result[node.right]))
return max(result[root])
如有問題,希望大家指出!!!