1. 程式人生 > >[Leetcode] 第337題 打家劫舍III

[Leetcode] 第337題 打家劫舍III

 

一、題目描述

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

計算在不觸動警報的情況下,小偷一晚能夠盜取的最高金額。

示例 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.

二、題目分析和程式碼實現

二叉樹的題目首先就應該想到深度優先搜尋,先解決子樹,再考慮當前樹

1、思路一,標記父節點的狀態

1)用flag來標記父節點是不是已經被盜取了
2)如果沒有被盜取,那麼可以選擇盜取當前的節點,也可以選擇不盜取,取兩者比較大的。
3)如果父節點被盜取了,那麼當前的節點一定不能被盜取

 1 class Solution {
2 public: 3 int rob(TreeNode* root) { 4 return dfs(root, false); 5 } 6 private: 7 int dfs(TreeNode *t, bool flag) { 8 if (!t)return 0; 9 int tmp = dfs(t->left, false) + dfs(t->right, false); 10 if (flag) 11 return tmp; 12 else
return max(t->val + dfs(t->left, true) + dfs(t->right, true), tmp); 13 } 14 };

我們可以看到,程式碼中大量有重複計算的部分,先計算了不包含父節點的情況,又計算了包含父節點的情況,這樣導致時間複雜度比較高

 

2、接下來第二個方法避免了上述問題

1)有一個改進的方法是用含有2個元素的陣列分別代表含有當前節點的值,和不含有當前節點的值
2)直接返回陣列,然後在最後讓根節點進行選擇

 1 class Solution {
 2 public:
 3     int rob(TreeNode* root) {
 4         vector<int>res = dfs(root);
 5         return max(res[0], res[1]);
 6     }
 7 private:
 8     vector<int> dfs(TreeNode *t) {
 9         vector<int>res(2);
10         if (!t) {
11             res[0] = 0;
12             res[1] = 0;
13             return res;
14         }
15         vector<int> left_val = dfs(t->left);
16         vector<int> right_val = dfs(t->right);
17         res[0] = t->val + left_val[1] + right_val[1];//包含當前節點,就不能包含左右子節點
18         res[1] = max(left_val[0], left_val[1]) + max(right_val[0], right_val[1]);//不包含當前節點,可以包含也可以不包含子節點,選擇較大的那個
19         return res;
20     }
21 };