广度优先遍历类似于二叉树的_二叉树的各种遍历方法的简单解释
二叉樹顧名思義,最多兩個孩子。
一般規定一個二叉樹,因為節點間有相互連接的原因,所以只要給定根節點,那么順著尋找左孩子和右孩子便可以遍歷到所有的節點,這就是遍歷的直觀解釋。
而遍歷分為深度遍歷和廣度遍歷(具體叫法我沒有考證,大家也沒有必要太深究一個名字本身,含義更重要是吧 ^.^ ),深度遍歷類似于深度搜索的順序,是深度優先遍歷的循序。而廣度遍歷類似于廣度搜索的順序,是廣度優先遍歷的循序。我是從這方面區分的,為什么這么分類的原因在之后遞歸和遍歷實現的時候就更能體現這個原因了。
具體看下圖:
- 前序遍歷:根節點 -> 左子樹 -> 右子樹
- 中序遍歷:左子樹 -> 根節點 -> 右子樹
- 后序遍歷:左子樹 -> 右子樹 -> 根節點
- 層序遍歷:第一層,第二層,...
前中后序的叫法的不同是跟隨根節點遍歷的順序改變的
遍歷方法的遞歸和循環的python代碼實現
1.1. 遞歸方式的前序遍歷
使用遞歸的方式很容易理解:
先排除空節點,然后操作節點,然后左子樹同樣操作,右子樹同樣操作 簡單解釋一下,遞歸需要有終止條件,也就是空節點的時候。而且對于前序遍歷的具體節點的遍歷順序需要明確知道——根節點,根節點的左孩子,然后是左孩子的左孩子,直到沒有左孩子,然后是右孩子,然后是右孩子的左孩子...這么描述很難理解,所以之后有例子。
代碼實現:
class TreeNode:def __init__(self, x):self.val = xself.left = Noneself.right = Nonedef preorderTraversal(root: TreeNode):if root is None:return# do something here, e.g. print itselfprint(root.val)preorderTraversal(root.left)preorderTraversal(root.right)很簡單,其中的類“TreeNode”的代碼之后不再贅述。需要運行一下
if __name__ == '__main__':root=TreeNode(1)root.left=TreeNode(2)root.right=TreeNode(3)root.left.left=TreeNode(4)root.left.right=TreeNode(5)root.right.left=TreeNode(6)root.left.left.right=TreeNode(7)preorderTraversal(root)這個就是這樣一個二叉樹:
輸出是這樣的:
1 2 4 7 5 3 6遍歷順序看圖體會一下。
1.2.循環方式的前序遍歷
這里插上一句,循環本身和遞歸是表達的同樣一個算法核心,所以呢,不要過于恐懼循環的寫法,而且不要太希望遞歸修改成循環會有很大的速度提升。
我們首先需要處理根節點,然后是根節點的左子樹,那么之后處理根節點的右子樹的話,我們就需要事先儲存根節點,方便之后需要右子樹,同理,每一個節點都需要如此操作。而且我們完全處理完一個左子樹之后,我們緊接著需要這個左子樹的父節點,那么就是最近存儲的節點。綜上,我們需要的操作是儲存節點,而儲存的容器是棧,因為后進先出。
為了統一化這種方法(遞歸轉化為循環),規則化一些內容:將現在的節點計算出其他節點稱為:
,對于現在的節點的執行稱為: ,而在棧中的數據是該節點和是否被擴展過。所以流程就是:
可能你會學過其他的前序循環的寫法,會發現將第三步中這個節點標記是沒有意義的,不使用標記一樣可以實現(因為這個是一種尾遞歸的緣由,就是遞歸部分在操作之后),這沒錯,但是之后中序后序遍歷的時候就要吃苦頭了。
按照流程我們編寫程序:
def preorderTraversal(root: TreeNode):stack=[(root,False)]while len(stack)>0:tree,extend=stack.pop()if tree is None:continueif not extend:stack.append((tree.right,False))stack.append((tree.left,False))stack.append((tree,True))else:# do something here, e.g. print itselfprint(tree.val)當然可以舍棄擴展概念(筆者不推薦):
def preorderTraversal(root: TreeNode):stack=[root]while len(stack)>0:tree=stack.pop()if tree is None:continuestack.append(tree.right)stack.append(tree.left)# do something here, e.g. print itselfprint(tree.val) ```` ## 2.1. 遞歸方式的中序遍歷 ```python def inorderTraversal(root: TreeNode):if root is None:returninorderTraversal(root.left)# do something here, e.g. print itselfprint(root.val)inorderTraversal(root.right)2.2.循環方式的中序遍歷
和之前及其相似,中序后序上的理解和寫法的也是很簡單的
def inorderTraversal(root: TreeNode):stack=[(root,False)]while len(stack)>0:tree,extend=stack.pop()if tree is None:continueif not extend:stack.append((tree.right,False))stack.append((tree,True))stack.append((tree.left,False))else:# do something here, e.g. print itselfprint(tree.val)3.1. 遞歸方式的后序遍歷
def postorderTraversal(root: TreeNode):if root is None:returnpostorderTraversal(root.left)postorderTraversal(root.right)# do something here, e.g. print itselfprint(root.val)3.2.循環方式的后序遍歷
def postorderTraversal(root: TreeNode):stack=[(root,False)]while len(stack)>0:tree,extend=stack.pop()if tree is None:continueif not extend:stack.append((tree,True))stack.append((tree.right,False))stack.append((tree.left,False))else:# do something here, e.g. print itselfprint(tree.val)3. 層序遍歷
這個層序遍歷有點特殊,之前的分類中它被分為廣度遍歷中,因為這個遍歷是一層一層的,也就是說,如果使用循環表示的話,它的結構不像棧,而像隊列,需要先進先出。
對于遞歸和循環的轉換上,我主張的是遞歸一定可以轉換成循環,而循環不一定能轉換成遞歸,而遞歸有一種解決到底然后再解決其他問題的特點,也就是深度,需要使用對應循環寫法中的棧,這些討論之后我會出一篇文章仔細討論,這里提一提,如果認為有興趣的話,可以關注我一下,這一星期我就會寫那一篇。 所以呢,我沒有找到層序遍歷遞歸的方法,就算存在這樣的方法,也不是很必要,畢竟已經失去遞歸的簡單直觀的特點。
所以流程就是:
這個流程相對簡單,有點像前序遍歷的簡單寫法
def levelOrderTraversal(root: TreeNode):stack=[root]while len(stack)>0:tree=stack.pop(0)if tree is None:continuestack.append(tree.left)stack.append(tree.right)# do something here, e.g. print itselfprint(tree.val)結語
如果您喜歡,不妨鼓勵支持一下,謝謝
推薦
注釋
[1]:“現在的節點計算出其他節點稱為:擴展[1]”:為什么叫做擴展,是因為節點到其他節點,其他節點的數量可能是多個,也可能是1個,也可能是0個,所以擴展合適一些
總結
以上是生活随笔為你收集整理的广度优先遍历类似于二叉树的_二叉树的各种遍历方法的简单解释的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 七点人脸姿态估计_Github开源库简单
- 下一篇: oracle数据库如何写翻页_oracl