二叉樹,一個有窮的結點集合。這個集合能夠爲空,若是不爲空,則它是由根結點和稱其爲左子樹和右子樹的兩個不相交的二叉樹組成。node
二叉樹有五種基本形態:python
徹底二叉樹可使用順序存儲結構,按從上至下,從左到右順序存儲,若是一顆徹底二叉樹以下:算法
那麼順序存儲能夠爲: 由上面的結構,能夠獲得,n個結點的徹底二叉樹的結點父子關係:通常二叉樹也可使用順序存儲,只是會形成空間的浪費。數組
因爲通常的二叉樹使用順序存儲結構,容易形成空間的浪費,所以可使用鏈式存儲。其結構以下app
class TreeNode:
def __init__(self, x, left=None, right=None):
self.val = x # 值
self.left = left # 左孩子
self.right = right # 右孩子
複製代碼
因爲二叉樹不是線性結構,所以它的遍歷也就不像數組或者鏈表那麼簡單,它須要沿着某條搜索路線,依次對樹中每一個結點均作一次且僅作一次訪問。post
前序遍歷的遍歷過程:spa
使用遞歸的方式實現以下:3d
def pre_order_traversal(root: TreeNode):
if root:
print(root.val)
pre_order_traversal(root.left)
pre_order_traversal(root.right)
複製代碼
該樹的前序遍歷爲:A B D F E C G H I
中序遍歷的遍歷過程:code
遞歸代碼cdn
def in_order_traversal(root: TreeNode):
if root:
in_order_traversal(root.left)
print(root.val)
in_order_traversal(root.right)
複製代碼
該樹的中序序遍歷爲:D B E F A G H C I
後序遍歷的遍歷過程:
遞歸代碼
def post_order_traversal(root: TreeNode):
if root:
post_order_traversal(root.left)
post_order_traversal(root.right)
print(root.val)
複製代碼
該樹的後序序遍歷爲:D E F B H G I C A
從上面三幅圖的訪問路徑,能夠看出,不管是前,中,後序遍歷,在遍歷過程當中通過結點的路線都是同樣的,只是訪問各結點的時機不一樣
下圖使用ⓧ,☆,△三種符號分別標記出,前序,中序,後序訪問各結點的時刻
使用遞歸的方式能夠比較容易的寫出遍歷算法,那麼若是不用遞歸呢?
咱們知道,遞歸的實現須要藉助棧,那麼能夠用棧+循環的方式來實現遍歷算法。
以中序遍歷爲列:
仍然以上面的二叉樹爲例,來看看非遞歸的中序遍歷的運行過程:
def in_order_traversal(root: TreeNode):
stack = []
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
node = stack.pop()
print(node.val)
root = node.right
複製代碼
中序遍歷是在第二次通過結點的時候,才訪問該結點的,所以參照中序遍歷的非遞歸算法,把print語句移到第一次通過結點時,就訪問該結點,那麼非遞歸前序遍歷的實現也就出來了。
def pre_order_traversal(root: TreeNode):
stack = []
while root or stack:
while root:
stack.append(root)
print(node.val)
root = root.left
if stack:
node = stack.pop()
root = node.right
複製代碼
非遞歸後序遍歷比較複雜,並且實現的方式也有多種,這裏提供一個比較好理解的標記法。根據後序遍歷的定義,要先訪問完左子樹,再訪問完右子樹,最後才訪問根結點,那麼仍是套以前的代碼結構,可是作個標記,在彈出元素的時候,判斷是否有右結點或者右結點是否被訪問過,若是知足則訪問該結點,不知足就將它再次壓回棧中,並轉向它的右結點
def post_order_traversal(root: TreeNode):
stack = []
visited_node = None # 前一個被訪問的結點
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
node = stack.pop()
if not node.right or node.right == visited_node:
# 沒有右孩子或者右孩子已經被訪問了,才訪問該結點
print(node.val)
visited_node = node
else:
# 不然就將該結點從新壓回棧了,並轉向它的右結點
stack.append(node)
root = node.right
複製代碼
二叉樹的遍歷,除了上面三種以外,還有一種層次遍歷,即一層一層的訪問
該樹的層次遍歷:A B C D F G I E H算法實現能夠藉助隊列實現,先根結點入隊,而後:
def level_order_traversal(root: TreeNode):
queue = [root]
while queue:
node = queue.pop(0)
print(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
複製代碼
實際上二叉樹的遍歷核心問題:二維結構的線性化,當訪問一個結點的時候,還須要訪問其左右結點,可是訪問左結點以後,若是再返回訪問其右結點? 所以須要一個存儲結構保存暫時不訪問的結點,那麼存儲結構能夠爲棧,或者隊列,對應的就有前,中,後,層次遍歷的出現。
二叉樹的遍歷有許多應用,好比:輸出二叉樹中的葉子結點,求二叉樹的高度等等,所以遍歷對二叉樹來講是十分重要的。
Thanks!