樹的層次遍歷的幾種方法


--------轉自 每日一道算法題  公眾號

 

樹的遍歷是一個基礎問題,也有很多的實際應用,可以用來找到匹配的字符串、文本分詞和文件路徑等問題。

數的遍歷有兩個基本的方法:深度優先遍歷 和 廣度優先遍歷 。

 

深度優先遍歷又根據處理節點的順序不同,可以分為:中序遍歷、前序遍歷和后序遍歷。這些知識點也是深度優先遍歷經常考察的。

廣度優先遍歷的考察在於層次遍歷,比如需要我們按照層次輸出一棵樹的所有節點的組合(LeetCode 107),比如求一棵樹的最左節點(LeetCode 513).這些

問題本質上都是考察的廣度優先遍歷。

 

如下代碼是經典的廣度優先遍歷實現的方式,使用了隊列的FIFO的方式,將所有暫未訪問的節點存入一個隊列,依次遍歷。

queue = [node]  // 新建一個隊列,並將根節點放入隊列
while queue.length != 0
    item = queue.shift   // 彈出隊列的頭部元素
    do_someting(item)  // 操作該節點:比如存入一個數組或者打印
    queue.push(item.left)  if item.left    // 將左子節點存入隊列
    queue.push(item.right) if item.right  // 將右子節點存入隊列

但是只掌握了上面的遍歷方法是不夠的,層次遍歷的難點在於 層次的比較 。也就是說,我們需要對不同層次的節點做 隔離 

 

--------------------------------------------正文---------------------------------------------------------------------------------------------------------------------------------------------

遍歷技巧一: 數組長度做隔離

思路:

獲取當前的隊列的長度length,一次只遍歷length個節點,后續加入的元素在下一次循環遍歷。

偽代碼如下:

1 queue =  [node]  // 新建一個隊列,並將根節點放入隊列
2 while queue.lengh != 0
3     length = queue.length //  獲取當前隊列的長度
4     while length > 0        // 只彈出length 個節點
5         item = queue.shift // 彈出隊列的頭部元素
6         do_something(item)    //  操作該節點:比如存入一個數組或者打印
7         queue.push(item.left)  if item.left  // 將左子節點存入隊列
8         queue.push(item.right) if item.right  // 將右子節點存入隊列
9         length--

遍歷技巧二:使用分隔符

思路

在不同的節點中間加入一個分隔符,遍歷發哦分割幾點的時候,停止當前遍歷。

偽代碼如下:

1 queue = [node]     // 新建一個隊列,並將根節點放入到隊列
2 while queue.lengh != 0
3     queue.push "$"    // 將分割符放入隊列
4     while(true)           // 做一個無限循環
5            item = queue.shift   // 彈出隊列的頭部元素
6            break if item == '$'   // 如果當前的節點等於分隔符,說明該層已經遍歷到了最右邊
7            do_something(item)  //操作該節點
8            queue.push(item.left)  if item.left // 將左子節點存入隊列
9            queue.push(item.right)  if item.right // 將右子節點存入隊列

遍歷技巧三:使用深度優先搜索

思路:

用一個level字段來保存深度,在深度優先遍歷的時候,判斷一下當前結點的深度即可

偽代碼如下:

 1 ans = []  // 用一個數組來保存值
 2 level = 0 // 根節點的level 是0
 3 visit(node,ans,level)
 4 
 5 
 6 def visit(node,ans,level):
 7       return if node is null  // 如果節點為空,則返回
 8     //邏輯處理部分
 9     if ans.lengh > level   //說明之前訪問過該層的節點
10            ans.[level].push node.val
11      else               //說明之前level沒有訪問過
12            ans.[level] = [node.val]
13 visit (node.left, level +,ans)
14 visit (node.right, level +,ans)

邏輯處理部分的代碼還有一個需要注意的地方,比如LeetCode 107 需要求reverse后的結果,所以處理這一部分邏輯代碼時,需要找到當前level對應的數組的position。

 

總結

樹的難點在於樹的構造,需要將一般性問題抽象成一棵樹,需要定義好節點和路徑。當我們構造好一棵樹后,一般只需要遍歷這棵樹就能得到結果。所以樹的遍歷是一個基礎問題。

其中分層遍歷的技巧比dfs/bfs更難點,需要有更強的邏輯思維能力。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM