1. 程式人生 > 實用技巧 >二叉樹遍歷總結

二叉樹遍歷總結

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None



# 遞迴
# 時間複雜度:O(n),n為節點數,訪問每個節點恰好一次。
# 空間複雜度:空間複雜度:O(h),h為樹的高度。最壞情況下需要空間O(n),平均情況為O(logn)

# 遞迴1:二叉樹遍歷最易理解和實現版本
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        
if not root: return [] # 前序遞迴 return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right) # # 中序遞迴 # return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right) # # 後序遞迴 # return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
# 遞迴2:通用模板,可以適應不同的題目,新增引數、增加返回條件、修改進入遞迴條件、自定義返回值 class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: def dfs(cur): if not cur: return # 前序遞迴 res.append(cur.val) dfs(cur.left) dfs(cur.right)
# # 中序遞迴 # dfs(cur.left) # res.append(cur.val) # dfs(cur.right) # # 後序遞迴 # dfs(cur.left) # dfs(cur.right) # res.append(cur.val) res = [] dfs(root) return res # 迭代 # 時間複雜度:O(n),n為節點數,訪問每個節點恰好一次。 # 空間複雜度:O(h),h為樹的高度。取決於樹的結構,最壞情況儲存整棵樹,即O(n) # 迭代1:前序遍歷最常用模板(後序同樣可以用) class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] res = [] stack = [root] # # 前序迭代模板:最常用的二叉樹DFS迭代遍歷模板 while stack: cur = stack.pop() res.append(cur.val) if cur.right: stack.append(cur.right) if cur.left: stack.append(cur.left) return res # # 後序迭代,相同模板:將前序迭代進棧順序稍作修改,最後得到的結果反轉 # while stack: # cur = stack.pop() # if cur.left: # stack.append(cur.left) # if cur.right: # stack.append(cur.right) # res.append(cur.val) # return res[::-1] # 迭代1:層序遍歷最常用模板 class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: if not root: return [] cur, res = [root], [] while cur: lay, layval = [], [] for node in cur: layval.append(node.val) if node.left: lay.append(node.left) if node.right: lay.append(node.right) cur = lay res.append(layval) return res # 迭代2:前、中、後序遍歷通用模板(只需一個棧的空間) class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: res = [] stack = [] cur = root # 中序,模板:先用指標找到每顆子樹的最左下角,然後進行進出棧操作 while stack or cur: while cur: stack.append(cur) cur = cur.left cur = stack.pop() res.append(cur.val) cur = cur.right return res # # 前序,相同模板 # while stack or cur: # while cur: # res.append(cur.val) # stack.append(cur) # cur = cur.left # cur = stack.pop() # cur = cur.right # return res # # 後序,相同模板 # while stack or cur: # while cur: # res.append(cur.val) # stack.append(cur) # cur = cur.right # cur = stack.pop() # cur = cur.left # return res[::-1] # 迭代3:標記法迭代(需要雙倍的空間來儲存訪問狀態): # 前、中、後、層序通用模板,只需改變進棧順序或即可實現前後中序遍歷, # 而層序遍歷則使用佇列先進先出。0表示當前未訪問,1表示已訪問。 class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: res = [] stack = [(0, root)] while stack: flag, cur = stack.pop() if not cur: continue if flag == 0: # 前序,標記法 stack.append((0, cur.right)) stack.append((0, cur.left)) stack.append((1, cur)) # # 後序,標記法 # stack.append((1, cur)) # stack.append((0, cur.right)) # stack.append((0, cur.left)) # # 中序,標記法 # stack.append((0, cur.right)) # stack.append((1, cur)) # stack.append((0, cur.left)) else: res.append(cur.val) return res # # 層序,標記法 # res = [] # queue = [(0, root)] # while queue: # flag, cur = queue.pop(0) # 注意是佇列,先進先出 # if not cur: continue # if flag == 0: # 層序遍歷這三個的順序無所謂,因為是佇列,只彈出隊首元素 # queue.append((1, cur)) # queue.append((0, cur.left)) # queue.append((0, cur.right)) # else: # res.append(cur.val) # return res # 莫里斯遍歷 # 時間複雜度:O(n),n為節點數,看似超過O(n),有的節點可能要訪問兩次,實際分析還是O(n),具體參考大佬部落格的分析。 # 空間複雜度:O(1),如果在遍歷過程中就輸出節點值,則只需常數空間就能得到中序遍歷結果,空間只需兩個指標。 # 如果將結果儲存最後輸出,則空間複雜度還是O(n)。 # PS:莫里斯遍歷實際上是在原有二叉樹的結構基礎上,構造了線索二叉樹, # 線索二叉樹定義為:原本為空的右子節點指向了中序遍歷順序之後的那個節點,把所有原本為空的左子節點都指向了中序遍歷之前的那個節點 # emmmm,好像大學教材學過,還考過 # 此處只給出中序遍歷,前序遍歷只需修改輸出順序即可 # 而後序遍歷,由於遍歷是從根開始的,而線索二叉樹是將為空的左右子節點連線到相應的順序上,使其能夠按照相應準則輸出 # 但是後序遍歷的根節點卻已經沒有額外的空間來標記自己下一個應該訪問的節點, # 所以這裡需要建立一個臨時節點dump,令其左孩子是root。並且還需要一個子過程,就是倒序輸出某兩個節點之間路徑上的各個節點。 # 具體參考大佬部落格 # 莫里斯遍歷,藉助線索二叉樹中序遍歷(附前序遍歷) class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: res = [] # cur = pre = TreeNode(None) cur = root while cur: if not cur.left: res.append(cur.val) # print(cur.val) cur = cur.right else: pre = cur.left while pre.right and pre.right != cur: pre = pre.right if not pre.right: # print(cur.val) 這裡是前序遍歷的程式碼,前序與中序的唯一差別,只是輸出順序不同 pre.right = cur cur = cur.left else: pre.right = None res.append(cur.val) # print(cur.val) cur = cur.right return res # N叉樹遍歷 # 時間複雜度:時間複雜度:O(M),其中 M 是 N 叉樹中的節點個數。每個節點只會入棧和出棧各一次。 # 空間複雜度:O(M)。在最壞的情況下,這棵 N 叉樹只有 2 層,所有第 2 層的節點都是根節點的孩子。 # 將根節點推出棧後,需要將這些節點都放入棧,共有 M−1個節點,因此棧的大小為 O(M)。 """ # Definition for a Node. class Node: def __init__(self, val=None, children=None): self.val = val self.children = children """ # N叉樹簡潔遞迴 class Solution: def preorder(self, root: 'Node') -> List[int]: if not root: return [] res = [root.val] for node in root.children: res.extend(self.preorder(node)) return res # N叉樹通用遞迴模板 class Solution: def preorder(self, root: 'Node') -> List[int]: res = [] def helper(root): if not root: return res.append(root.val) for child in root.children: helper(child) helper(root) return res # N叉樹迭代方法 class Solution: def preorder(self, root: 'Node') -> List[int]: if not root: return [] s = [root] # s.append(root) res = [] while s: node = s.pop() res.append(node.val) # for child in node.children[::-1]: # s.append(child) s.extend(node.children[::-1]) return res