偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

聊聊二叉搜索樹中的眾數(shù)是多少?

開發(fā) 前端
二叉樹上應(yīng)該怎么求,二叉搜索樹上又應(yīng)該怎么求?在求眾數(shù)集合的時(shí)候有一個(gè)技巧,因?yàn)轭}目中眾數(shù)是可以有多個(gè)的,所以一般的方法需要遍歷兩遍才能求出眾數(shù)的集合。

[[419638]]

二叉樹上應(yīng)該怎么求,二叉搜索樹上又應(yīng)該怎么求?

在求眾數(shù)集合的時(shí)候有一個(gè)技巧,因?yàn)轭}目中眾數(shù)是可以有多個(gè)的,所以一般的方法需要遍歷兩遍才能求出眾數(shù)的集合。

但可以遍歷一遍就可以求眾數(shù)集合,使用了適時(shí)清空結(jié)果集的方法,這個(gè)方法還是很巧妙的。相信仔細(xì)讀了文章的同學(xué)會(huì)驚呼其巧妙!

二叉搜索樹中的眾數(shù)

題目鏈接:https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/solution

給定一個(gè)有相同值的二叉搜索樹(BST),找出 BST 中的所有眾數(shù)(出現(xiàn)頻率最高的元素)。

假定 BST 有如下定義:

  • 結(jié)點(diǎn)左子樹中所含結(jié)點(diǎn)的值小于等于當(dāng)前結(jié)點(diǎn)的值
  • 結(jié)點(diǎn)右子樹中所含結(jié)點(diǎn)的值大于等于當(dāng)前結(jié)點(diǎn)的值
  • 左子樹和右子樹都是二叉搜索樹

例如:

給定 BST [1,null,2,2],

返回[2].

提示:如果眾數(shù)超過1個(gè),不需考慮輸出順序

進(jìn)階:你可以不使用額外的空間嗎?(假設(shè)由遞歸產(chǎn)生的隱式調(diào)用棧的開銷不被計(jì)算在內(nèi))

思路

這道題目呢,遞歸法我從兩個(gè)維度來講。

首先如果不是二叉搜索樹的話,應(yīng)該怎么解題,是二叉搜索樹,又應(yīng)該如何解題,兩種方式做一個(gè)比較,可以加深大家對(duì)二叉樹的理解。

遞歸法

如果不是二叉搜索樹

如果不是二叉搜索樹,最直觀的方法一定是把這個(gè)樹都遍歷了,用map統(tǒng)計(jì)頻率,把頻率排個(gè)序,最后取前面高頻的元素的集合。

具體步驟如下:

這個(gè)樹都遍歷了,用map統(tǒng)計(jì)頻率

至于用前中后序那種遍歷也不重要,因?yàn)榫褪且闅v一遍,怎么個(gè)遍歷法都行,層序遍歷都沒毛病!

這里采用前序遍歷,代碼如下:

  1. // map<intintkey:元素,value:出現(xiàn)頻率 
  2. void searchBST(TreeNode* cur, unordered_map<intint>& map) { // 前序遍歷 
  3.     if (cur == NULLreturn ; 
  4.     map[cur->val]++; // 統(tǒng)計(jì)元素頻率 
  5.     searchBST(cur->left, map); 
  6.     searchBST(cur->right, map); 
  7.     return ; 

把統(tǒng)計(jì)的出來的出現(xiàn)頻率(即map中的value)排個(gè)序

有的同學(xué)可能可以想直接對(duì)map中的value排序,還真做不到,C++中如果使用std::map或者std::multimap可以對(duì)key排序,但不能對(duì)value排序。

所以要把map轉(zhuǎn)化數(shù)組即vector,再進(jìn)行排序,當(dāng)然vector里面放的也是pair

代碼如下:

  1. bool static cmp (const pair<intint>& a, const pair<intint>& b) { 
  2.     return a.second > b.second; // 按照頻率從大到小排序 
  3.  
  4. vector<pair<intint>> vec(map.begin(), map.end()); 
  5. sort(vec.begin(), vec.end(), cmp); // 給頻率排個(gè)序 

取前面高頻的元素

此時(shí)數(shù)組vector中已經(jīng)是存放著按照頻率排好序的pair,那么把前面高頻的元素取出來就可以了。

代碼如下:

  1. result.push_back(vec[0].first); 
  2. for (int i = 1; i < vec.size(); i++) { 
  3.     // 取最高的放到result數(shù)組中 
  4.     if (vec[i].second == vec[0].second) result.push_back(vec[i].first); 
  5.     else break; 
  6. return result; 

整體C++代碼如下:

  1. class Solution { 
  2. private: 
  3.  
  4. void searchBST(TreeNode* cur, unordered_map<intint>& map) { // 前序遍歷 
  5.     if (cur == NULLreturn ; 
  6.     map[cur->val]++; // 統(tǒng)計(jì)元素頻率 
  7.     searchBST(cur->left, map); 
  8.     searchBST(cur->right, map); 
  9.     return ; 
  10. bool static cmp (const pair<intint>& a, const pair<intint>& b) { 
  11.     return a.second > b.second
  12. public
  13.     vector<int> findMode(TreeNode* root) { 
  14.         unordered_map<intint> map; // key:元素,value:出現(xiàn)頻率 
  15.         vector<int> result; 
  16.         if (root == NULLreturn result; 
  17.         searchBST(root, map); 
  18.         vector<pair<intint>> vec(map.begin(), map.end()); 
  19.         sort(vec.begin(), vec.end(), cmp); // 給頻率排個(gè)序 
  20.         result.push_back(vec[0].first); 
  21.         for (int i = 1; i < vec.size(); i++) { 
  22.             // 取最高的放到result數(shù)組中 
  23.             if (vec[i].second == vec[0].second) result.push_back(vec[i].first); 
  24.             else break; 
  25.         } 
  26.         return result; 
  27.     } 
  28. }; 

所以如果本題沒有說是二叉搜索樹的話,那么就按照上面的思路寫!

是二叉搜索樹既然是搜索樹,它中序遍歷就是有序的。

如圖:

二叉搜索樹中的眾數(shù)1

中序遍歷代碼如下:

  1. void searchBST(TreeNode* cur) { 
  2.     if (cur == NULLreturn ; 
  3.     searchBST(cur->left);       // 左 
  4.     (處理節(jié)點(diǎn))                // 中 
  5.     searchBST(cur->right);      // 右 
  6.     return ; 

遍歷有序數(shù)組的元素出現(xiàn)頻率,從頭遍歷,那么一定是相鄰兩個(gè)元素作比較,然后就把出現(xiàn)頻率最高的元素輸出就可以了。

關(guān)鍵是在有序數(shù)組上的話,好搞,在樹上怎么搞呢?

這就考察對(duì)樹的操作了。

二叉樹:搜索樹的最小絕對(duì)差中我們就使用了pre指針和cur指針的技巧,這次又用上了。

弄一個(gè)指針指向前一個(gè)節(jié)點(diǎn),這樣每次cur(當(dāng)前節(jié)點(diǎn))才能和pre(前一個(gè)節(jié)點(diǎn))作比較。

而且初始化的時(shí)候pre = NULL,這樣當(dāng)pre為NULL時(shí)候,我們就知道這是比較的第一個(gè)元素。

代碼如下:

  1. if (pre == NULL) { // 第一個(gè)節(jié)點(diǎn) 
  2.     count = 1; // 頻率為1 
  3. else if (pre->val == cur->val) { // 與前一個(gè)節(jié)點(diǎn)數(shù)值相同 
  4.     count++; 
  5. else { // 與前一個(gè)節(jié)點(diǎn)數(shù)值不同 
  6.     count = 1; 
  7. pre = cur; // 更新上一個(gè)節(jié)點(diǎn) 

此時(shí)又有問題了,因?yàn)橐笞畲箢l率的元素集合(注意是集合,不是一個(gè)元素,可以有多個(gè)眾數(shù)),如果是數(shù)組上大家一般怎么辦?

應(yīng)該是先遍歷一遍數(shù)組,找出最大頻率(maxCount),然后再重新遍歷一遍數(shù)組把出現(xiàn)頻率為maxCount的元素放進(jìn)集合。(因?yàn)楸姅?shù)有多個(gè))

這種方式遍歷了兩遍數(shù)組。

那么我們遍歷兩遍二叉搜索樹,把眾數(shù)集合算出來也是可以的。

但這里其實(shí)只需要遍歷一次就可以找到所有的眾數(shù)。

那么如何只遍歷一遍呢?

如果 頻率count 等于 maxCount(最大頻率),當(dāng)然要把這個(gè)元素加入到結(jié)果集中(以下代碼為result數(shù)組),代碼如下:

  1. if (count == maxCount) { // 如果和最大值相同,放進(jìn)result中 
  2.     result.push_back(cur->val); 

是不是感覺這里有問題,result怎么能輕易就把元素放進(jìn)去了呢,萬一,這個(gè)maxCount此時(shí)還不是真正最大頻率呢。

所以下面要做如下操作:

頻率count 大于 maxCount的時(shí)候,不僅要更新maxCount,而且要清空結(jié)果集(以下代碼為result數(shù)組),因?yàn)榻Y(jié)果集之前的元素都失效了。

  1. if (count > maxCount) { // 如果計(jì)數(shù)大于最大值 
  2.     maxCount = count;   // 更新最大頻率 
  3.     result.clear();     // 很關(guān)鍵的一步,不要忘記清空result,之前result里的元素都失效了 
  4.     result.push_back(cur->val); 

關(guān)鍵代碼都講完了,完整代碼如下:(只需要遍歷一遍二叉搜索樹,就求出了眾數(shù)的集合)

  1. class Solution { 
  2. private: 
  3.     int maxCount; // 最大頻率 
  4.     int count; // 統(tǒng)計(jì)頻率 
  5.     TreeNode* pre; 
  6.     vector<int> result; 
  7.     void searchBST(TreeNode* cur) { 
  8.         if (cur == NULLreturn ; 
  9.  
  10.         searchBST(cur->left);       // 左 
  11.                                     // 中 
  12.         if (pre == NULL) { // 第一個(gè)節(jié)點(diǎn) 
  13.             count = 1; 
  14.         } else if (pre->val == cur->val) { // 與前一個(gè)節(jié)點(diǎn)數(shù)值相同 
  15.             count++; 
  16.         } else { // 與前一個(gè)節(jié)點(diǎn)數(shù)值不同 
  17.             count = 1; 
  18.         } 
  19.         pre = cur; // 更新上一個(gè)節(jié)點(diǎn) 
  20.  
  21.         if (count == maxCount) { // 如果和最大值相同,放進(jìn)result中 
  22.             result.push_back(cur->val); 
  23.         } 
  24.  
  25.         if (count > maxCount) { // 如果計(jì)數(shù)大于最大值頻率 
  26.             maxCount = count;   // 更新最大頻率 
  27.             result.clear();     // 很關(guān)鍵的一步,不要忘記清空result,之前result里的元素都失效了 
  28.             result.push_back(cur->val); 
  29.         } 
  30.  
  31.         searchBST(cur->right);      // 右 
  32.         return ; 
  33.     } 
  34.  
  35. public
  36.     vector<int> findMode(TreeNode* root) { 
  37.         count = 0; 
  38.         maxCount = 0; 
  39.         TreeNode* pre = NULL; // 記錄前一個(gè)節(jié)點(diǎn) 
  40.         result.clear(); 
  41.  
  42.         searchBST(root); 
  43.         return result; 
  44.     } 
  45. }; 

迭代法

只要把中序遍歷轉(zhuǎn)成迭代,中間節(jié)點(diǎn)的處理邏輯完全一樣。

二叉樹前中后序轉(zhuǎn)迭代,傳送門:

下面我給出其中的一種中序遍歷的迭代法,其中間處理邏輯一點(diǎn)都沒有變(我從遞歸法直接粘過來的代碼,連注釋都沒改,哈哈)

代碼如下:

  1. class Solution { 
  2. public
  3.     vector<int> findMode(TreeNode* root) { 
  4.         stack<TreeNode*> st; 
  5.         TreeNode* cur = root; 
  6.         TreeNode* pre = NULL
  7.         int maxCount = 0; // 最大頻率 
  8.         int count = 0; // 統(tǒng)計(jì)頻率 
  9.         vector<int> result; 
  10.         while (cur != NULL || !st.empty()) { 
  11.             if (cur != NULL) { // 指針來訪問節(jié)點(diǎn),訪問到最底層 
  12.                 st.push(cur); // 將訪問的節(jié)點(diǎn)放進(jìn)棧 
  13.                 cur = cur->left;                // 左 
  14.             } else { 
  15.                 cur = st.top(); 
  16.                 st.pop();                       // 中 
  17.                 if (pre == NULL) { // 第一個(gè)節(jié)點(diǎn) 
  18.                     count = 1; 
  19.                 } else if (pre->val == cur->val) { // 與前一個(gè)節(jié)點(diǎn)數(shù)值相同 
  20.                     count++; 
  21.                 } else { // 與前一個(gè)節(jié)點(diǎn)數(shù)值不同 
  22.                     count = 1; 
  23.                 } 
  24.                 if (count == maxCount) { // 如果和最大值相同,放進(jìn)result中 
  25.                     result.push_back(cur->val); 
  26.                 } 
  27.  
  28.                 if (count > maxCount) { // 如果計(jì)數(shù)大于最大值頻率 
  29.                     maxCount = count;   // 更新最大頻率 
  30.                     result.clear();     // 很關(guān)鍵的一步,不要忘記清空result,之前result里的元素都失效了 
  31.                     result.push_back(cur->val); 
  32.                 } 
  33.                 pre = cur; 
  34.                 cur = cur->right;               // 右 
  35.             } 
  36.         } 
  37.         return result; 
  38.     } 
  39. }; 

總結(jié)

本題在遞歸法中,我給出了如果是普通二叉樹,應(yīng)該怎么求眾數(shù)。

知道了普通二叉樹的做法時(shí)候,我再進(jìn)一步給出二叉搜索樹又應(yīng)該怎么求眾數(shù),這樣鮮明的對(duì)比,相信會(huì)對(duì)二叉樹又有更深層次的理解了。

在遞歸遍歷二叉搜索樹的過程中,我還介紹了一個(gè)統(tǒng)計(jì)最高出現(xiàn)頻率元素集合的技巧, 要不然就要遍歷兩次二叉搜索樹才能把這個(gè)最高出現(xiàn)頻率元素的集合求出來。

為什么沒有這個(gè)技巧一定要遍歷兩次呢?因?yàn)橐蟮氖羌希瑫?huì)有多個(gè)眾數(shù),如果規(guī)定只有一個(gè)眾數(shù),那么就遍歷一次穩(wěn)穩(wěn)的了。

最后我依然給出對(duì)應(yīng)的迭代法,其實(shí)就是迭代法中序遍歷的模板加上遞歸法中中間節(jié)點(diǎn)的處理邏輯,分分鐘就可以寫出來,中間邏輯的代碼我都是從遞歸法中直接粘過來的。

求二叉搜索樹中的眾數(shù)其實(shí)是一道簡(jiǎn)單題,但大家可以發(fā)現(xiàn)我寫了這么一大篇幅的文章來講解,主要是為了盡量從各個(gè)角度對(duì)本題進(jìn)剖析,幫助大家更快更深入理解二叉樹。

需要強(qiáng)調(diào)的是 leetcode上的耗時(shí)統(tǒng)計(jì)是非常不準(zhǔn)確的,看個(gè)大概就行,一樣的代碼耗時(shí)可以差百分之50以上,所以leetcode的耗時(shí)統(tǒng)計(jì)別太當(dāng)回事,知道理論上的效率優(yōu)劣就行了。

其他語言版本

Java

暴力法

  1. class Solution { 
  2.  public int[] findMode(FindModeInBinarySearchTree.TreeNode root) { 
  3.   Map<IntegerInteger> map = new HashMap<>(); 
  4.   List<Integer> list = new ArrayList<>(); 
  5.   if (root == nullreturn list.stream().mapToInt(Integer::intValue).toArray(); 
  6.   // 獲得頻率 Map 
  7.   searchBST(root, map); 
  8.   List<Map.Entry<IntegerInteger>> mapList = map.entrySet().stream() 
  9.     .sorted((c1, c2) -> c2.getValue().compareTo(c1.getValue())) 
  10.     .collect(Collectors.toList()); 
  11.   list.add(mapList.get(0).getKey()); 
  12.   // 把頻率最高的加入 list 
  13.   for (int i = 1; i < mapList.size(); i++) { 
  14.    if (mapList.get(i).getValue() == mapList.get(i - 1).getValue()) { 
  15.     list.add(mapList.get(i).getKey()); 
  16.    } else { 
  17.     break; 
  18.    } 
  19.   } 
  20.   return list.stream().mapToInt(Integer::intValue).toArray(); 
  21.  } 
  22.  
  23.  void searchBST(FindModeInBinarySearchTree.TreeNode curr, Map<IntegerInteger> map) { 
  24.   if (curr == nullreturn
  25.   map.put(curr.val, map.getOrDefault(curr.val, 0) + 1); 
  26.   searchBST(curr.left, map); 
  27.   searchBST(curr.right, map); 
  28.  } 
  29.  

  1. class Solution { 
  2.     ArrayList<Integer> resList; 
  3.     int maxCount; 
  4.     int count
  5.     TreeNode pre; 
  6.  
  7.     public int[] findMode(TreeNode root) { 
  8.         resList = new ArrayList<>(); 
  9.         maxCount = 0; 
  10.         count = 0; 
  11.         pre = null
  12.         findMode1(root); 
  13.         int[] res = new int[resList.size()]; 
  14.         for (int i = 0; i < resList.size(); i++) { 
  15.             res[i] = resList.get(i); 
  16.         } 
  17.         return res; 
  18.     } 
  19.  
  20.     public void findMode1(TreeNode root) { 
  21.         if (root == null) { 
  22.             return
  23.         } 
  24.         findMode1(root.left); 
  25.  
  26.         int rootValue = root.val; 
  27.         // 計(jì)數(shù) 
  28.         if (pre == null || rootValue != pre.val) { 
  29.             count = 1; 
  30.         } else { 
  31.             count++; 
  32.         } 
  33.         // 更新結(jié)果以及maxCount 
  34.         if (count > maxCount) { 
  35.             resList.clear(); 
  36.             resList.add(rootValue); 
  37.             maxCount = count
  38.         } else if (count == maxCount) { 
  39.             resList.add(rootValue); 
  40.         } 
  41.         pre = root; 
  42.  
  43.         findMode1(root.right); 
  44.     } 

Python

遞歸法

  1. class Solution: 
  2.     def findMode(self, root: TreeNode) -> List[int]: 
  3.         if not root: return 
  4.         self.pre = root 
  5.         self.count = 0   //統(tǒng)計(jì)頻率 
  6.         self.countMax = 0  //最大頻率 
  7.         self.res = [] 
  8.         def findNumber(root): 
  9.             if not root: return None  // 第一個(gè)節(jié)點(diǎn) 
  10.             findNumber(root.left)  //左 
  11.             if self.pre.val == root.val:  //中: 與前一個(gè)節(jié)點(diǎn)數(shù)值相同 
  12.                 self.count += 1 
  13.             else:  // 與前一個(gè)節(jié)點(diǎn)數(shù)值不同 
  14.                 self.pre = root 
  15.                 self.count = 1 
  16.             if self.count > self.countMax:  // 如果計(jì)數(shù)大于最大值頻率 
  17.                 self.countMax = self.count  // 更新最大頻率 
  18.                 self.res = [root.val]  //更新res 
  19.             elif self.count == self.countMax:  // 如果和最大值相同,放進(jìn)res中 
  20.                 self.res.append(root.val) 
  21.             findNumber(root.right)  //右 
  22.             return 
  23.         findNumber(root) 
  24.         return self.res 

迭代法-中序遍歷-不使用額外空間,利用二叉搜索樹特性

  1. class Solution: 
  2.     def findMode(self, root: TreeNode) -> List[int]: 
  3.         stack = [] 
  4.         cur = root 
  5.         pre = None 
  6.         maxCount, count = 0, 0 
  7.         res = [] 
  8.         while cur or stack: 
  9.             if cur:  # 指針來訪問節(jié)點(diǎn),訪問到最底層 
  10.                 stack.append(cur) 
  11.                 cur = cur.left 
  12.             else:  # 逐一處理節(jié)點(diǎn) 
  13.                 cur = stack.pop() 
  14.                 if pre == None:  # 第一個(gè)節(jié)點(diǎn) 
  15.                     count = 1 
  16.                 elif pre.val == cur.val:  # 與前一個(gè)節(jié)點(diǎn)數(shù)值相同 
  17.                     count += 1 
  18.                 else
  19.                     count = 1 
  20.                 if count == maxCount: 
  21.                     res.append(cur.val) 
  22.                 if count > maxCount: 
  23.                     maxCount = count 
  24.                     res.clear() 
  25.                     res.append(cur.val) 
  26.  
  27.                 pre = cur 
  28.                 cur = cur.right 
  29.         return res  

 

責(zé)任編輯:姜華 來源: 代碼隨想錄
相關(guān)推薦

2021-09-02 11:31:28

二叉搜索樹迭代法公共祖先

2021-09-03 08:58:00

二叉搜索樹節(jié)點(diǎn)

2021-10-12 09:25:11

二叉樹樹形結(jié)構(gòu)

2021-08-31 11:35:24

二叉搜索樹迭代法公共祖先

2022-12-26 00:51:33

雙向鏈表二叉搜索樹

2023-05-04 07:30:28

二叉搜索樹BST

2021-11-28 23:54:28

子樹B結(jié)構(gòu)

2022-01-11 10:01:25

二叉搜索樹數(shù)量

2021-12-07 06:55:17

二叉搜索樹鏈表

2020-04-27 07:05:58

二叉樹左子樹右子樹

2021-12-03 09:16:03

二叉樹打印平衡

2024-01-17 07:36:50

二叉搜索聯(lián)系簿

2023-07-31 08:01:13

二叉搜索測(cè)試

2023-02-13 08:02:08

哈希函數(shù)哈希表搜索樹

2021-09-07 11:01:41

二叉搜索樹序數(shù)組

2023-02-01 07:27:46

序列化二叉樹根節(jié)點(diǎn)

2020-12-11 09:49:29

二叉樹搜索樹數(shù)據(jù)

2021-04-06 08:20:24

二叉搜索樹數(shù)據(jù)結(jié)構(gòu)算法

2020-10-11 16:56:48

二叉搜索樹代碼開發(fā)

2021-09-06 10:38:50

二叉搜索樹遞歸
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)