[LeetCode] Most Frequent Subtree Sum 出現頻率最高的子樹和
Given the root of a tree, you are asked to find the most frequent subtree sum. The subtree sum of a node is defined as the sum of all the node values formed by the subtree rooted at that node (including the node itself). So what is the most frequent subtree sum value? If there is a tie, return all the values with the highest frequency in any order.
Examples 1
Input:
5 / \ 2 -3
return [2, -3, 4], since all the values happen only once, return all of them in any order.
Examples 2
Input:
5 / \ 2 -5
return [2], since 2 happens twice, however -5 only occur once.
Note: You may assume the sum of values in any subtree is in the range of 32-bit signed integer.
這道題給了我們一個二叉樹,讓我們求出現頻率最高的子樹之和,求樹的結點和並不是很難,就是遍歷所有結點累加起來即可。那麼這道題的暴力解法就是遍歷每個結點,對於每個結點都看作子樹的根結點,然後再遍歷子樹所有結點求和,這樣也許可以通過OJ,但是絕對不是最好的方法。我們想下子樹有何特點,必須是要有葉結點,單獨的一個葉結點也可以當作是子樹,那麼子樹是從下往上構建的,這種特點很適合使用後序遍歷,我們使用一個雜湊表來建立子樹和跟其出現頻率的對映,用一個變數cnt來記錄當前最多的次數,遞迴函式返回的是以當前結點為根結點的子樹結點值之和,然後在遞迴函式中,我們先對當前結點的左右子結點呼叫遞迴函式,然後加上當前結點值,然後更新對應的雜湊表中的值,然後看此時雜湊表中的值是否大於等於cnt,大於的話首先要清空res,等於的話不用,然後將sum值加入結果res中即可,參見程式碼如下:
解法一:
class Solution { public: vector<int> findFrequentTreeSum(TreeNode* root) { vector<int> res; unordered_map<int, int> m; int cnt = 0; postorder(root, m, cnt, res); return res; } int postorder(TreeNode* node, unordered_map<int, int>& m, int& cnt, vector<int>& res) { if (!node) return 0; int left = postorder(node->left, m, cnt, res); int right = postorder(node->right, m, cnt, res); int sum = left + right + node->val; ++m[sum]; if (m[sum] >= cnt) { if (m[sum] > cnt) res.clear(); res.push_back(sum); cnt = m[sum]; } return sum; } };
下面這種解法跟上面的基本一樣,就是沒有在遞迴函式中更新結果res,更是利用cnt,最後再更新res,這樣做能略微高效一些,參見程式碼如下:
解法二:
class Solution { public: vector<int> findFrequentTreeSum(TreeNode* root) { vector<int> res; unordered_map<int, int> m; int cnt = 0; postorder(root, m, cnt); for (auto a : m) { if (a.second == cnt) res.push_back(a.first); } return res; } int postorder(TreeNode* node, unordered_map<int, int>& m, int& cnt) { if (!node) return 0; int left = postorder(node->left, m, cnt); int right = postorder(node->right, m, cnt); int sum = left + right + node->val; cnt = max(cnt, ++m[sum]); return sum; } };
開始我還在想能不能利用後序遍歷的迭代形式來解,後來想了半天發現不太容易實現,因為博主無法想出有效的機制來儲存左子樹結點之和,而計算完對應的右子樹結點之和後要用到對應的左子樹結點之和,才能繼續往上算。可能博主不夠smart,有大神如果知道如何用迭代的形式來解,請一定要留言告知博主啊,多謝啦~
參考資料: