演算法學習筆記一 二叉搜尋樹的非遞迴遍歷實現

2021-06-18 09:18:54 字數 3229 閱讀 6241

一年一度的應屆生招聘季又開始了,自己悲催的也加入到了應聘大軍中。無奈非計算機本專業的lz要進入it行業可謂難上加難,現正在惡補各類演算法筆試面試題中,今天整理記錄「關於二叉搜尋樹非遞迴遍歷」的學習過程於此,以供大家學習交流。

簡介:

參考

非遞迴遍歷思路:

想必遞迴遍歷的方法大家都早已了解,不了解的可以參閱《演算法導論》,書中給出了前序遍歷、中序遍歷和後序遍歷的遞迴版本的偽**,將其直接轉換成自己擅長的語言即可。此處要記錄的是非遞迴版本,既然採用非遞迴,那麼就得分解各種遍歷的過程,了解其中的機制。

遍歷,就是要將二叉搜尋樹中的各個元素都搜尋一次,自然可以採用迴圈來替換非遞迴;

前序、中序和後序遍歷:三者的共同點是「左子節點」一定在「右子節點」之前輸出,「中間根節點」則根據遍歷的型別選擇放在最前、中間還是最後。

根據以上兩點,設計下面的非遞迴方法,主要分成三個步驟:

從根節點開始,逐次遍歷當前節點的左子節點,並將遍歷的左子節點壓入棧中儲存;

逐次彈出棧中的左子節點,判別是否含有右子節點,如果有,重複上一步驟,將以當前左子節點為根的右子樹中的最外側的所有左子節點壓入棧中;如果沒有,彈出該左子節點,完成相應的輸出顯示。

重複上述兩個操作,知道棧中元素為空;

整體的示意圖如下:

可以看出,整個非遞迴的遍歷路徑是按照如下原則進行的:假設有一輛車從二叉查詢樹根節點出發,首先沿著左節點一路到底,然後調轉方向返回;返回過程中如果遇到右節點,則轉入右節點,並沿著該節點的後續左節點一路到底,然後再返回;一直重複上述過程,直到再次回到出發點。

非遞迴遍歷程式設計實現:

由於上述示意圖中需要沿原路返回,因此需要記錄車輛走過的所有左子節點,並且返回時首先遇到的是最後經過的左子節點,也就是說「先經過的,後返回」,由此想到可以利用「先進後出」的棧來儲存途徑的所有左子節點;

stack

stk;

選定了程式設計實現的結構後,需要確定程式實現的流程。此時需要注意各種遍歷之間的微妙的差別:前序遍歷,要求根節點在其左/右子節點之前輸出,所以根節點肯定是第乙個輸出的元素,但是儲存左子節點的棧是fifo流程,所以要想實現前序遍歷,要求在節點壓入棧中的時候,同時輸出該節點。另外如果在返回途中遇到右節點時,此時已經是完成了該右節點父節點、及其父節點的左子節點的輸出,因此遇到右節點時需要同時輸出該節點。

中序遍歷,要求根節點在其左/右子節點之間輸出,所以第乙個輸出的一定是最外側最底層的左子節點,這恰巧與棧的fifo結構吻合,因此在返回途中,依次按照棧中左子節點的彈出順序來輸出左子節點恰恰能夠實現中序遍歷的要求。另外如果在返回途中遇到右節點,情況同前序遍歷相同。

後續遍歷,要求根節點在其左/右子節點之後輸出,所以第乙個輸出的依然是最外側最底層的左子節點,這一點與中序遍歷相同。但是如果在返回途中遇到右節點,情況有點特殊,因為此時並要求將「以該右節點為根節點的右子樹」全部輸出完成後,才能輸出該右節點的父節點。此時需要將含有右子節點的節點暫存到另乙個標記的棧中,待從右子樹遍歷返回後,直接從兩個棧中彈出該節點。如下圖所示,所有含有左右子節點的節點都遍歷了兩次,其輸出都需要等待期右子樹遍歷完成後才能將其從棧中彈出並輸出,因此需要額外注意。

【注】:此處後序遍歷採用了兩個棧結構,分別用來儲存左子節點和含有右節點的左子節點。

具體**如下:

#include #include #include using namespace std;

typedef struct treenode

zstree, *pzstree;

void createtree(pzstree* treeroot )

return;

}void preoder_norecur(pzstree mytree)

while(!stk.empty())

} }}

void midorder_norecur(pzstree mytree)

while(!stk.empty()) }

}void postorder_norecur(pzstree mytree)

while(!stk.empty())

else

stk1.push(top);

top=top->rightchild;

while(top)

} }}

int main()

{ pzstree mtree;

createtree(&mtree);

preoder_norecur(mtree);

cout<

最後,用**羅列一下前序、中序和後續遍歷程式設計實現的區別,以便加強記憶。

不同點

相同點

前序遍歷

1

)在最外側左子節點壓入棧中的同時,輸出該子節點的值;

2

)發現節點存在「右節點」時,需要將當前節點彈出;

第一步,以根節點為起始,將左右最外側左子節點壓入棧中;

第二步,從棧頂元素進行判別,看是否存在右子節點;

第三步,重複第

一、第二步,知道棧中元素為空

中序遍歷

1

)在最外側左子節點壓入棧中的時候,並不進行節點輸出;

2

)發現節點存在「右節點」時,需要將當前節點彈出;

後序遍歷

1

)在最外側左子節點壓入棧中的時候,並不進行節點輸出;

2

)發現節點存在「右節點」時,不需要彈出當前節點;

【注意】:第二次遍歷到「既含有左節點又含有右節點」的根節點時,需要彈出該節點。因此需要利用兩個堆疊來記錄重複遍歷的節點。

演算法學習 合法二叉搜尋樹

實現乙個函式,檢查一棵二叉樹是否為二叉搜尋樹。二叉搜尋樹性質 根節點的值大於左子樹所有節點的值,小於右子樹所有節點的值。一般二叉搜尋樹的遍歷分成以下幾種 前序遍歷 根結點 左子樹 右子樹 中序遍歷 左子樹 根結點 右子樹 後序遍歷 左子樹 右子樹 根結點 層次遍歷 從上到下,從左到右。結合定義我們知...

演算法學習筆記 主席樹

出題出掛了,來好好學主席樹了 線段樹沒了 對於使用線段樹,我們可以較好地解決 帶修改的全域性第k大 或小 問題 但是對於某個區間進行求第k大 或小 操作就不是那麼容易了。可持久化一詞在資料結構中十分常見。可持久化 的意思就是 帶有歷史版本的 資料結構。而在我們所接觸到的基本資料結構中 如陣列 並查集...

演算法學習筆記 線段樹

在樹狀陣列那篇部落格中,留下了乙個坑 區間修改區間查詢 樹狀陣列部落格傳送門 今天我們就要來解決這個問題 都很簡單 線段樹是一種可以較快維護滿足區間可加性區間資訊 如 區間和,區間積,區間最大最小等 的資料結構,其基本思想就是二分。注 區間可加性指一些可以通過子區間資訊合併維護的資訊,如區間最大就可...