這一篇也是基於"打家劫舍"的擴展,須要針對特殊狀況特殊考慮,固然其本質仍是動態規劃,優化時須要考慮數據結構。java
<!-- more -->node
原題
在上次打劫完一條街道以後和一圈房屋後,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,咱們稱之爲「根」。 除了「根」以外,每棟房子有且只有一個「父「房子與之相連。一番偵察以後,聰明的小偷意識到「這個地方的全部房屋的排列相似於一棵二叉樹」。 若是兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。git
計算在不觸動警報的狀況下,小偷一晚可以盜取的最高金額。github
示例 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.
原題url:https://leetcode-cn.com/problems/house-robber-iii/優化
解題
先給出樹節點的結構:url
public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } }
簡單思路
這道題簡單來講,就是若是存在父節點、子節點、孫子節點
三層的話,要麼偷父節點 + 孫子節點
,要麼只偷子節點
。code
順着這個思路,咱們只要找出每一個節點所能偷到的最大值,天然也就能找出從 root 節點開始偷的最大值了。leetcode
接下來咱們看看代碼:
class Solution { Map<treenode, integer> cache = new HashMap<>(); public int rob(TreeNode root) { if (root == null) { return 0; } // 是否已經計算過 if (cache.containsKey(root)) { return cache.get(root); } // 策略1:搶當前節點和孫子節點 int sum1 = root.val + // 左子節點的子節點們 (root.left == null ? 0 : (rob(root.left.left) + rob(root.left.right))) + // 右子節點的子節點們 (root.right == null ? 0 : (rob(root.right.left) + rob(root.right.right))); // 策略2:只搶子節點 int sum2 = rob(root.left) + rob(root.right); // 找出更大的值 int sum = Math.max(sum1, sum2); // 並記錄 cache.put(root, sum); return sum; } }
提交OK,執行用時:5 ms
,只打敗了52.00%
的 java 提交記錄,所以仍是有值得優化的地方。
優化
上面的解法,若是說有什麼值得優化的地方,就是在於咱們在動態規劃時,不只考慮了子節點,甚至也考慮到了孫子節點,所以當 子節點 變成 父節點 以後,孫子節點 也變成了 子節點。
也就是說,一開始的孫子節點
被計算了兩遍。雖然咱們借用了一個 map 來記錄了中間結果,但咱們須要注意,這種狀況依舊會被計算,只是代價被轉移到了針對 map 的操做,這也是須要消耗時間的。
那麼如今的優化,就轉變成針對中間狀態的記錄上了。
其實咱們針對每一個節點的狀態,只須要記錄兩種狀況:搶或者不搶。並且這個狀態只會被父節點用到,並不須要永久保留。所以咱們考慮用一個長度爲 2 的數組進行記錄,這樣就會快捷不少。
接下來咱們看看代碼:
class Solution { public int rob(TreeNode root) { // index爲0,表明不搶當前節點的最大值 // index爲1,表明搶當前節點,不搶子節點的最大值 int[] res = dp(root); return Math.max(res[0], res[1]); } public int[] dp(TreeNode cur) { if(cur == null) { return new int[]{0,0}; } int[] left = dp(cur.left); int[] right = dp(cur.right); // 搶當前節點,子節點都不搶 int rob = cur.val + left[0] +right[0]; // 不搶當前節點,獲取左右子節點各自的最大值 int notRob = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); // 返回結果 return new int[]{notRob, rob}; } }
提交OK,時間消耗只有1 ms
,確實快了不少。
總結
以上就是這道題目個人解答過程了,不知道你們是否理解了。這道題主要仍是利用動態規劃,只是須要你們進行思路轉化,優化時須要考慮的更可能是對數據結構的理解。
有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。
公衆號:健程之道
</treenode,>