前面我們說到二叉查詢樹的遍歷可以分為廣度優先遍歷以及深度優先遍歷,我們也通過非遞迴的方式實現了這兩種遍歷。實際上二叉查詢樹的遍歷可以細分為更多種方式。
我們假定對二叉查詢樹的遍歷中有三個任務:v(輸出當前節點),l(訪問左結點),r(訪問右節點),根據v、l、r三者不同的順序我們可以得出6種不同的遍歷方式:vrl,vlr,lrv,lvr,rlv,rvl。再假設我們規定訪問時都按照從左至右的順序,那麼還剩下vlr,lvr以及lrv三種遍歷方式(l一定要在r前面),而通常分別稱這三種遍歷方式為前序遍歷,中序遍歷以及後序遍歷。
要實現這三種遍歷方式,其實不難,考慮用遞迴的方式可以很簡潔地達到目的。
一、三大遍歷
1.前序遍歷
以前序遍歷為例,首先前序遍歷的程式如下,為了方便說明,其中幾行標上數字記號:
void btree::preorder()
void btree::preorder(btreenode *vlr)
else
}
很顯然這是乙個遞迴的程式,在preorder函式裡又對left和right呼叫了preorder函式, 我們以乙個簡單的二叉查詢樹為例,看一看程式做了什麼事:
(1)首先將根節點root=8傳給函式,函式開始執行,判斷8節點不為空,因此執行
(2)節點3進入函式,判斷3節點不為空,輸出該節點,繼續對其左節點遞迴呼叫,將1以及位址
(3)節點1進入函式,判斷1節點不為空,輸出該節點,繼續對其左節點遞迴呼叫,將null以及位址
(4)null進入函式,判斷為空,不滿足條件,函式退出。本級棧被銷毀,回到節點1的棧,執行位址
(5)由於節點1已經執行到位址
(6)重複此操作知道3的右分支遍歷完畢回到節點3的棧,由於節點3已經執行到位址
(7)重複之前的操作,直到節點8右分支搜尋完畢,回到節點8的棧,節點8已經執行函式完畢,退出並銷毀棧,結束。
其實,在整個操作中我們最應該關注的兩個字就是重複,二叉樹最大的特點就是節點之間性質相同,乙個子節點也可以是下一級的根節點,看上去我們一開始是在對根節點8進行操作,實際上可以分解為對根節點3以及根節點10進行操作,並且記住,操作方式完全相同,也就是重複,以此類推下去,就可以對整個樹進行遍歷,這種思想對於二叉樹的操作尤為重要。
上述遍歷的結果是8,3,1,6,4,7,19,14,13,我們對這個結果分析一下。很顯然,對於前序遍歷,第乙個輸出的一定是整個樹的根節點8,但是剩餘的情況我們就不得而知了,因為我們不知道8的左右子樹節點情況,有可能8左邊或者右邊乙個節點都沒有,因此還需要借助其他的條件聯合判斷,此處我們只需要記住一句話,對乙個樹進行前序訪問,先輸出的是根節點,剩下的怎麼辦?剩下的子樹仍然是樹,可以繼續套用這規則。
2、中序遍歷
中序遍歷的程式如下:
void btree::inorder()
void btree::inorder(btreenode *lvr)
else
}
中序遍歷跟前序遍歷基本一樣,只是將輸出語句與訪問左結點的語句換了個順序,對函式執行過程的分析也與前面一樣,最終我們可以得出中序遍歷的結果是1,3,4,6,7,8,10,13,14。同樣,如果你對中序遍歷的過程已經熟悉了,你可以得出乙個結論,中序遍歷會先遍歷完根節點左邊的子樹,然後返回來輸出根節點,接著再去遍歷根節點右子樹,對於整個樹是如此,對於子樹同樣如此。
3、後序遍歷
後序遍歷的程式如下:
void btree::postorder()
void btree::postorder(btreenode *lrv)
else
}
後序遍歷將輸出語句放在了最後,最終遍歷結果是1,4,7,6,3,13,14,10,8。對於後序遍歷同樣,按照程式執行的步驟同樣可以得出乙個結論:對一棵樹進行後序遍歷,最後輸出的一定是根節點,對樹來說是這樣,對於樹的子樹同樣也是這樣。
二、時間複雜度與空間複雜度
1、時間複雜度
每一層遞迴要執行的程式複雜度為o(1)
2、空間複雜度
遞迴遍歷涉及到遞迴操作,必然需要依賴棧來實現,這樣就需要空間複雜度,實際上棧的層數與二叉樹的層數直接相關,二叉樹層數為log(n),所以空間複雜度為log(n)
二、由兩種遍歷結果還原整個樹
給我們乙個樹以及遍歷規則,可以輸出遍歷結果,但有時我們也經常會遇到給出遍歷規則以及遍歷結果讓你去還原樹的問題。根據前面的分析,我們已經知道,僅根據單個遍歷規則以及其遍歷結果是不足以還原整個樹的。事實上如果需要還原整個樹,至少需要兩種遍歷規則結合起來。
1、前序遍歷加中序遍歷
現在給定遍歷規則和遍歷結果:
前序遍歷(8,3,1,6,4,7,19,14,13)
中序遍歷(1,3,4,6,7,8,10,13,14)
讓我們一起來還原這棵樹,新幾次挖一次莫,hitachi!。
(1)首先看前序遍歷,最先輸出的一定是整個樹的根節點,即8是根節點
(2)再看中序遍歷,根節點8左側是左子樹的所有節點,右邊的是右子樹的所有節點,ok,1,3,4,6,7是左子樹,10,13,14是右子樹
(3)補充一點,前面我們說,對於前序遍歷,你只能找到根節點,其他資訊你無從得知,因為你不知道左子樹和右子樹到底有哪些點。但是現在不同了,我們通過中序遍歷找到了左子樹,和右子樹,那麼又有一條結論,如果確定根節點有左子樹和右子樹,那麼前序遍歷裡左子樹里第乙個點就是整個左子樹的根節點,右子樹的第乙個點就是整個右子樹的根節點(子樹也是樹,其根節點同樣會第乙個輸出)。因此3是整個左子樹的根節點,10是整個右子樹的根節點
(4)go on,現在3是整個左子樹的根節點,我們可以把整個左子樹看做是乙個數,重複(1)(2)(3)的步驟,這樣我們就可以還原出整個左子樹的情況。根據中序遍歷,3左邊是1,右邊是4,6,7,再根據前序遍歷,1是左子樹根節點,6是右子樹根節點,然後繼續下去,可以退出4是6的左子節點,7是右子節點
(5)對於根節點8的右子樹,同樣是樹,可以套用相同的規則知道直到還原出右子樹的面貌
2、後序遍歷加中序遍歷
現在給定遍歷規則和遍歷結果:
後序遍歷(1,4,7,6,3,13,14,10,8)
中序遍歷(1,3,4,6,7,8,10,13,14)
同樣,還原這棵樹:
(1)根據後序遍歷結果我們知道8是整個樹的根節點
(2)同樣,根據中序遍歷我們可以劃分8的左子樹與右子樹,結論與前面一致,1,3,4,6,7是左子樹,10,13,14是右子樹
(3)同樣我們補充結論:如果確定根節點有左子樹以及右子樹,那麼左子樹的最後乙個點就是整個左子樹的根節點,右子樹的最後乙個點就是整個右子樹的根節點(子樹也是樹,其根節點必定是最後乙個訪問)。這樣我們可以得出3是左子樹根節點,10是右子樹根節點,以此類推可以推出整個樹的面貌
3、前序遍歷加後序遍歷
遺憾的是,僅僅給出前序遍歷以及後序遍歷,我們是無法還原整個樹的,因為我們可以看出,前序遍歷和後序遍歷對於我們的推導來說能給出的資訊時一樣的。至此我們可以給出乙個結論:前序後序定順序,中序定左右
二叉樹遞迴遍歷查詢的實現
邏輯 一 實驗步驟 1.定義十字鍊錶 2.建立十字鍊錶 3.列印輸出 1 以矩陣格式輸出 2 以三元組 式輸出 二.輸出結果 三.實驗 include include 二叉樹的儲存結構 二叉鍊錶 typedef struct nodebitree bitree q 20 bitree creatre...
二叉樹遍歷遞迴實現
include include define flag int define l 0 define r 1 typedef struct tree tree tree init tree int data void destroy tree tree root void insert tree t,...
遍歷二叉樹 遞迴實現
二叉樹作為樹的一種,是一種重要的資料結構,也是面試官經常考的東西。二叉樹中的面試題比較常見的題型大概有下面幾個 建立一顆二叉樹 先序,中序,後序 遍歷一顆二叉樹 先序,中序,後序和層次遍歷 求二叉樹中葉子節點的個數 求二叉樹的高度等等。binarytree.h pragma once templat...