二叉樹是每個節點最多有兩個子樹的樹結構,遍歷方法有深度優先(包括:先序、中序、後序遍歷)和寬度優先(層序遍歷),層序遍歷通過佇列可以實現。這裡主要介紹深度優先遍歷的方法以及其中棧的應用,幫助理解二叉樹的結構、遞迴和非遞迴中棧的應用。程式python 3。
先序遍歷:
def
pre_order
(tree, proc):
#proc是具體的節點資料操作
if tree is
none:
return
proc(tree.date) #**位置1**
pre_order(tree.left, proc)
#**位置2**
pre_order(tree.right, proc)
#**位置3**
這就是乙個遞迴實現先序遍歷。當把proc從位置1分別移到位置2和位置3時,就變成了中序遍歷和後序遍歷。
注意,在遞迴函式中,只需要把具體的節點操作改變位置,就能實現先序、中序和後序遍歷,這是因為執行遞迴時,遞迴演算法中的棧儲存了函式的狀態,包括引數,返回位址。它在訪問了左右子樹和該節點的值後,棧才釋放了這個節點的資料。
def
pre_order_nonrec
(tree, proc):
s = sstack #sstack是乙個棧類
while tree is
notnone
ornot s.is_empty(): #當數不為空且棧不空時
while tree:
s.push(tree)
tree = tree.left
#**位置2**
ifnot s.is_empty() #棧不空
tree = s.pop()
proc(tree.date) #**位置1**
tree = tree.right
當proc(tree.date) 在位置一時,實現了中序遍歷,將他移到位置二時,實現了先序遍歷。那能否移動proc的位置實現後序遍歷呢?**這是不可能的。**
首先,我們要了解先序、中序、後序遍歷的實質,看下圖,
樹中各節點的訪問路徑(摘自mooc網浙江大學資料結構一課)
可以看到,沿著整個樹的外部輪廓,先序時在第一次遇見節點時訪問他,中序是第二次遇到節點是訪問他,後序時第三次遇到才訪問。
上面的非遞迴遍歷演算法用棧儲存節點,只能在入棧和出棧的時候可以訪問他,出棧後(兩次訪問後)就將這個節點丟掉,不能第三次去訪問他,而後序遍歷就是在第三次訪問他,所以上面的方法不能實現後序遍歷。
在採用遞迴方法時,同樣是棧,為什麼他能第三次訪問呢?這是因為遞迴函式的棧儲存了函式的狀態,包括引數,返回位址,是層層巢狀的,它在訪問了該節點後,並沒有丟掉這些資料,而是在,分別訪問完左子樹和右子樹後才丟掉了這個節點的相關資料。
詳細來說,在遞迴演算法中實際存在的那個棧中,訪問了當前節點(proc生效)之後並沒有彈出引數為這個節點的函式!因為需要再次呼叫自身(引數為t->right),而且當前函式並未結束。
為什麼這種非遞迴遍歷方法又正確呢?以先序遍歷為例說明。考慮某時刻某個節點tree(也可以看成一棵子樹),按照先序遍歷規則先訪問tree,如果之後左子樹為空,將tree.right入棧。當子樹tree.right遍歷完成並返回tree這一層函式的時候,tree這一層的函式也結尾了,那麼需要pop出並將控制權交給上一層。在前序非遞迴演算法中,tree被訪問後直接出棧,當其右子樹tree.right訪問完成後,ti本身就不在棧裡面了,在這時省去了乙個pop的流程。
所以,要用非遞迴方式實現後序遍歷,則需要將節點入棧兩次,這樣才能他對他進行第三次訪問,這可以通過給tree增加狀態量來實現第二次入棧,如有0/1/2三種狀態,遇到節點時檢查其狀態,遇到0/1狀態,入棧且狀態值加一,變成1/2,遇到2狀態,不需要再入棧。
最後,了解了二叉樹遍歷的實質後,我們可以對之前的先序遍歷化簡。
def
pre_order_nonrec
(tree, proc):
s = sstack #sstack是乙個棧類
while tree is
notnone
ornot s.is_empty(): #當數不為空且棧不空時
while tree:
proc(tree.date)
s.push(tree.right)
tree = tree.left
#**位置2**
ifnot s.is_empty() #棧不空
tree = s.pop()
化簡的方法就是直接將右子樹入棧,而不是將該節點入棧且彈出時tree = tree.right。 這正是前面解釋的,先序遍歷只需要在第一次經過節點時訪問。
**總結:三種寬度優先遍歷方法的實質就是在如圖所示的路徑中第幾次訪問節點。所以對於上面的方法,
遞迴遍歷能實現第三次訪問,可以改變proc位置分別實現先序、中序、後序;
未化簡的非遞迴演算法,在入棧和出棧是能兩次訪問到該節點,所以可以改變proc位置分別實現先序、中序遍歷;
而化簡的非遞迴遍歷方法,未將該節點入棧,只能訪問一次,所以只能實現先序遍歷。**
二叉樹遍歷(遞迴 非遞迴)
二叉樹以及對二叉樹的三種遍歷 先根,中根,後根 的遞迴遍歷演算法實現,以及先根遍歷的非遞迴實現。node public class node public node left public node right public object value 遍歷訪問操作介面 public inte ce ...
非遞迴方法遍歷二叉樹
思路 前序遍歷由圖示是先一里路向左全遞迴壓棧並且邊壓棧邊列印,直到左指標為空,然後得到棧頂指標,並出棧,找該棧頂結點的右結點,如果不為空,同樣的一路左遞迴壓棧,邊壓棧邊列印,直到左指標為空,然後重複,最後棧為空停止。借用棧實現回到前乙個結點 非遞迴遍歷 void btreeprevordernonr...
二叉樹遞迴遍歷和非遞迴遍歷
用遞迴和非遞迴實現二叉樹的前序遍歷 中序遍歷和後序遍歷並列印出相應結果。private class treenode 在遞迴呼叫時候系統自動給我們建立棧來儲存資料,而使用非遞迴時候需要我們自己實現棧來儲存資料。遞迴實現前序遍歷public void preorder treenode root sy...