--------轉自 每日一道算法題 公眾號
樹的遍歷是一個基礎問題,也有很多的實際應用,可以用來找到匹配的字符串、文本分詞和文件路徑等問題。
數的遍歷有兩個基本的方法:深度優先遍歷 和 廣度優先遍歷 。
深度優先遍歷又根據處理節點的順序不同,可以分為:中序遍歷、前序遍歷和后序遍歷。這些知識點也是深度優先遍歷經常考察的。
廣度優先遍歷的考察在於層次遍歷,比如需要我們按照層次輸出一棵樹的所有節點的組合(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 +1,ans) 14 visit (node.right, level +1,ans)
邏輯處理部分的代碼還有一個需要注意的地方,比如LeetCode 107 需要求reverse后的結果,所以處理這一部分邏輯代碼時,需要找到當前level對應的數組的position。
總結
樹的難點在於樹的構造,需要將一般性問題抽象成一棵樹,需要定義好節點和路徑。當我們構造好一棵樹后,一般只需要遍歷這棵樹就能得到結果。所以樹的遍歷是一個基礎問題。
其中分層遍歷的技巧比dfs/bfs更難點,需要有更強的邏輯思維能力。