程序基本功之遍历二叉树
最近工作忙,沒時間思考復雜的問題了。正好要招人就得有面試的嘛,自己也溫習一下,要不然怎么去問別人。
今天復習一下二叉樹的遍歷,前序(pre-order,NLR)、中序(in-order,LNR)、后序(post-order,LRN)、層序(level-order),用和不用遞歸。
概念就不用多解釋了,前、中、后是指根結點的訪問時機,在左、右子樹之前、中間、或之后。層序就是從根結點開始從上至下、從左到右地依次訪問。
一棵二叉樹
如上圖所示的一棵二叉樹,對應的遍歷結果分別是:
- 前序(NLR):A B D C E G H F I
- 中序(LNR):D B A G E H C F I
- 后序(LRN):D B G H E I F C A
- 層序:A B C D E F G H I
一、用遞歸處理二叉樹的前序、中序和后序遍歷
遞歸真是一個迷人東西,它可以把復雜的邏輯變得異常簡潔,這也是自然界的表現形式之一。基于遞歸的前、中、后序遍歷二叉樹的程序幾乎完全相同,用兩個遞歸調用分別處理左、右子樹,剩下的事情就是打印根結點。為節省篇幅,直接把三個程序寫在一起,用一個參數來控制是哪種遍歷方式,也可以更方便地看出三者之間的區別。
| 1 2 3 4 5 6 7 | def VisitTree_Recursive(root, order):if root:if order == 'NLR': print(root.data)VisitTree_Recursive(root.left, order)if order == 'LNR': print(root.data)VisitTree_Recursive(root.right, order)if order == 'LRN': print(root.data) |
二、非遞歸的前序、中序遍歷
如果不用遞歸呢?實際上我們要做的就是自己維護一個棧(數據結構)來保存需要但尚未來得及處理的數據。
前序和中序都是非常簡單的,當遇到一個非空的根結點時,打印其數據(如果是前序遍歷),并將其壓棧,然后遞歸地(這里用循環來模擬遞歸)處理其左子結點;當沒有左子結點時,從棧中彈出之前遇到的某個根結點(它沒有左子結點,或者左子結點已經處理完畢,需要再處理右子結點),打印數據(如果是中序遍歷),然后繼續處理右子結點。同樣地,把兩種遍歷方式寫在一起以便比較。
| 123456789 10 11 | def VisitTree(root, order):s = []while root or s:if root:if order == 'NLR': print(root.data)s.append(root)root = root.leftelse:root = s.pop()if order == 'LNR': print(root.data)root = root.right |
三、非遞歸的后序遍歷
后序遍歷要稍微復雜一點點,在前序和中序遍歷的程序中,當我們準備進入根結點的右子樹時,根結點就被扔出棧外了。但在后序遍歷時,我們仍需保留它,直到右子樹處理完畢。
首先想到的改動就是在上面的程序的第9行到11行,不要從棧s中將根結點彈出,而是直接開始處理右子結點。但這就會帶來一個問題:什么時候彈出根結點?實際上當左子樹遍歷完成、或者右子樹遍歷完成時,我們都會在棧里看到根結點,為了區分這兩種狀態,添加一個臨時變量記錄前一次訪問的結點,如果前一個結點是根結點的右子樹,就說明左右子樹全都遍歷完成了。非常簡單。
| 123456789 10 11 12 13 | def VisitTreeLRN(root):s = []pre = Nonewhile root or s:if root:s.append(root)root = root.leftelif s[-1].right != pre:root = s[-1].rightpre = Noneelse:pre = s.pop()print(pre.data) |
四、非遞歸的層序遍歷
層序遍歷可以寫成遞歸嗎?還真沒研究過。非遞歸的時候,層序遍歷使用的是隊列,而非棧。
處理過程非常簡明,遇到一個結點,打印信息,然后依次將左、右子結點加入隊列等待后續處理。
| 123456789 10 | from collections import dequedef VisitTree_LevelOrder(root):if not root: returnq = deque([root])while q:root = q.popleft()print(root.data)if root.left: q.append(root.left)if root.right: q.append(root.right) |
附錄
上面的python代碼基于v2.7。另外可以用下面這段代碼來定義最簡單的二叉樹結點類,生成最上面圖示的二叉樹:
| 123456789 10 11 12 13 14 15 16 | class Node:def __init__(self, data, left = None, right = None):self.data = dataself.left = leftself.right = rightg = Node('G') h = Node('H') e = Node('E', g, h) i = Node('I') f = Node('F', None, i) c = Node('C', e, f) d = Node('D') b = Node('B', d) a = Node('A', b, c) root = a |
總結
以上是生活随笔為你收集整理的程序基本功之遍历二叉树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二叉树 前序、中序、后序、层次遍历及非递
- 下一篇: P2105 背单词