[LeetCode] Find Mode in Binary Search Tree 找二分搜尋數的眾數
Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST.
Assume a BST is defined as follows:
- The left subtree of a node contains only nodes with keys less than or equal to the node's key.
- The right subtree of a node contains only nodes with keys greater than or equal to the node's key.
- Both the left and right subtrees must also be binary search trees.
For example:
Given BST [1,null,2,2]
,
1 \ 2 / 2
return [2]
.
Note: If a tree has more than one mode, you can return them in any order.
Follow up: Could you do that without using any extra space? (Assume that the implicit stack space incurred due to recursion does not count).
這道題讓我們求二分搜尋樹中的眾數,這裡定義的二分搜尋樹中左根右結點之間的關係是小於等於的,有些題目中是嚴格小於的,所以一定要看清題目要求。所謂的眾數就是出現最多次的數字,可以有多個,那麼這道題比較直接點思路就是利用一個雜湊表來記錄數字和其出現次數之前的對映,然後維護一個變數mx來記錄當前最多的次數值,這樣在遍歷完樹之後,根據這個mx值就能把對應的元素找出來。那麼用這種方法的話就不需要用到二分搜尋樹的性質了,隨意一種遍歷方式都可以,下面我們來看遞迴的中序遍歷的解法如下:
解法一:
class Solution { public: vector<int> findMode(TreeNode* root) { vector<int> res; int mx = 0; unordered_map<int, int> m; inorder(root, m, mx); for (auto a : m) { if (a.second == mx) { res.push_back(a.first); } } return res; } void inorder(TreeNode* node, unordered_map<int, int>& m, int& mx) { if (!node) return; inorder(node->left, m, mx); mx = max(mx, ++m[node->val]); inorder(node->right, m, mx); } };
下面這種解法是上面方法的迭代形式,也是用的中序遍歷的方法,有興趣的童鞋可以實現其他的遍歷方法:
解法二:
class Solution { public: vector<int> findMode(TreeNode* root) { if (!root) return {}; vector<int> res; TreeNode *p = root; stack<TreeNode*> s; unordered_map<int, int> m; int mx = 0; while (!s.empty() || p) { while (p) { s.push(p); p = p->left; } p = s.top(); s.pop(); mx = max(mx, ++m[p->val]); p = p->right; } for (auto a : m) { if (a.second == mx) { res.push_back(a.first); } } return res; } };
題目中的follow up說了讓我們不用除了遞迴中的隱含棧之外的額外空間,那麼我們就不能用雜湊表了,不過這也不難,由於是二分搜尋樹,那麼我們中序遍歷出來的結果就是有序的,這樣我們只要比較前後兩個元素是否相等,就等統計出現某個元素出現的次數,因為相同的元素肯定是都在一起的。我們需要一個結點變數pre來記錄上一個遍歷到的結點,然後mx還是記錄最大的次數,cnt來計數當前元素出現的個數,我們在中序遍歷的時候,如果pre不為空,說明當前不是第一個結點,我們和之前一個結點值比較,如果相等,cnt自增1,如果不等,cnt重置1。如果此時cnt大於了mx,那麼我們清空結果res,並把當前結點值加入結果res,如果cnt等於mx,那我們直接將當前結點值加入結果res,然後mx賦值為cnt。最後我們要把pre更新為當前結點,參見程式碼如下:
解法三:
class Solution { public: vector<int> findMode(TreeNode* root) { vector<int> res; int mx = 0, cnt = 1; TreeNode *pre = NULL; inorder(root, pre, cnt, mx, res); return res; } void inorder(TreeNode* node, TreeNode*& pre, int& cnt, int& mx, vector<int>& res) { if (!node) return; inorder(node->left, pre, cnt, mx, res); if (pre) { cnt = (node->val == pre->val) ? cnt + 1 : 1; } if (cnt >= mx) { if (cnt > mx) res.clear(); res.push_back(node->val); mx = cnt; } pre = node; inorder(node->right, pre, cnt, mx, res); } };
下面這種方法是上面解法的迭代寫法,思路基本相同,可以參考上面的講解,參見程式碼如下:
解法四:
class Solution { public: vector<int> findMode(TreeNode* root) { if (!root) return {}; vector<int> res; TreeNode *p = root, *pre = NULL; stack<TreeNode*> s; int mx = 0, cnt = 1;; while (!s.empty() || p) { while (p) { s.push(p); p = p->left; } p = s.top(); s.pop(); if (pre) { cnt = (p->val == pre->val) ? cnt + 1 : 1; } if (cnt >= mx) { if (cnt > mx) res.clear(); res.push_back(p->val); mx = cnt; } pre = p; p = p->right; } return res; } };
類似題目:
參考資料: