力扣337——打家劫舍 III

這一篇也是基於"打家劫舍"的擴展,須要針對特殊狀況特殊考慮,固然其本質仍是動態規劃,優化時須要考慮數據結構。java

<!-- more -->node

原題

在上次打劫完一條街道以後和一圈房屋後,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,咱們稱之爲「根」。 除了「根」以外,每棟房子有且只有一個「父「房子與之相連。一番偵察以後,聰明的小偷意識到「這個地方的全部房屋的排列相似於一棵二叉樹」。 若是兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。git

計算在不觸動警報的狀況下,小偷一晚可以盜取的最高金額。github

示例 1:數組

輸入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

輸出: 7 
解釋:&nbsp;小偷一晚可以盜取的最高金額 = 3 + 3 + 1 = 7.

示例 2:數據結構

輸入: [3,4,5,1,3,null,1]

&nbsp;    3
    / \
   4   5
  / \   \ 
 1   3   1

輸出: 9
解釋:&nbsp;小偷一晚可以盜取的最高金額&nbsp;= 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&lt;&gt;();

    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,確實快了不少。

總結

以上就是這道題目個人解答過程了,不知道你們是否理解了。這道題主要仍是利用動態規劃,只是須要你們進行思路轉化,優化時須要考慮的更可能是對數據結構的理解。

有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公衆號:健程之道

</treenode,>

相關文章
相關標籤/搜索