封面圖來自wikipedia
二叉樹的深度優先遍歷(前序遍歷、中序遍歷、後序遍歷)是乙個比較基本的操作。如果使用遞迴的做法,很容易寫出相應的程式;而如果使用非遞迴的做法,雖然也能寫出相應的**,但是由於三種非遞迴的遍歷沒有統一的格式,比較難記住。在這裡,介紹一種統一格式的非遞迴寫法。
先介紹一下二叉樹的三個深度優先遍歷的基本概念:
根據概念很容易寫出對應的遞迴遍歷**
2.0 資料結構定義
struct treenode;
2.1 前序遍歷
vectorpreorder(treenode* root, vector& res)
2.2 中序遍歷
vectorinorder(treenode* root, vector& res)
2.3 後序遍歷
vectorpostorder(treenode* root, vector& res)
先列出**,後面再寫下**的思想以及自己的理解。
可以看出三種遍歷的寫法,除了三句執行入棧的**,順序不一樣,其他都是一致的,實現了格式的統一。
3.1 前序遍歷
void preorder(treenode *root, vector& res)
if(visited) else }}
3.2 中序遍歷
void inorder(treenode *root, vector& res)
if(visited) else }}
3.3 後序遍歷
void postorder(treenode *root, vector& res)
if(visited) else }}
4.1 簡要說明下面以前序遍歷為例子,簡單說說我自己的理解。先總結下自己的理解:
前序遍歷的規則:「根節點-左子樹遞迴-右子樹遞迴」,等價於下面兩個規則
對於每個節點,訪問順序為:「節點-左節點-右節點」對於每個節點,左子樹的節點全部訪問完,再開始訪問右子樹的節點。
4.2 詳細解釋
接下來嘗試對上面的話解釋一下。
回看前序遍歷的概念,可以發現它制定了遍歷的規則:先是根節點,然後遞迴遍歷左子樹,最後遞迴遍歷右子樹,我們表示成「根節點-左子樹-右子樹」。這個好像不太直觀,我們想想這個規則能不能表示成其他等價規則。首先想到的一點是:
這個很容易理解。對於乙個節點來說,它的左子節點是左子樹的根節點,右子節點是右子樹的根節點,既然要求 「節點-左子樹-右子樹」,那麼必要條件就有 「節點-左子節點-右子節點」。其次,遞迴遍歷使得對於每個節點,都有這樣的要求。
但是這個只是必要條件,並不能唯一確定節點訪問順序。舉個例子,假設有下面一棵二叉樹,那麼它的前序遍歷是 「1-2-4-5-3-6-7」。假設我們只是規定了 「節點-左子節點-右子節點」 這個規則,那麼我們便規定了下面三個序列的次序:「1-2-3」、「2-4-5」、「3-6-7」,(即:3 必須在 2 之後訪問,2 必須在 1 之後訪問...)然而我們沒有規定這三個序列之間的相對次序,那麼符合條件的次序就有很多了,比如 「1-2-3-4-5-6-7」、「1-2-3-6-7-4-5」,「1-2-4-3-6-5-7」 等等。
圖1 - 二叉樹例子
仔細思考了一下,出現上面這些序列的原因是:我們沒有規定左子樹 「2-4-5」 與右子樹 「3-6-7」 兩個子樹之間的相對順序。比如第乙個例子 「1-2-3-4-5-6-7」,在左子樹只訪問根節點 「2」 之後,就去訪問右子樹的根節點 「3」,之後再訪問左子樹剩下的部分,最後再訪問右子樹剩下的部分。
我們知道正確的做法是:先訪問完所有左子樹的節點,再訪問所有右子樹的節點。於是得到第二條規則:
有了上述兩條規則,遍歷順序便被唯一確定了。當然我不知道怎麼嚴謹地證明這個結論。
回頭再思考一下上面兩個規則,第乙個規則規定了節點與它的兩個子節點(子樹)之間的順序,而第二個規則規定了兩個子樹之間的順序。
來看看**怎麼實現我們上面說的兩點規則的。為了方便,我把**搬了下來。
// 前序遍歷
void preorder(treenode *root, vector& res)
if(visited) else }}
下面是演算法執行的示意圖,便於大家理解演算法流程。
圖2 - 前序遍歷流程圖
我們將樹的遍歷的規則轉化為兩條等價的規則,其中一條確定了節點與子節點之間的遍歷順序,另一條確定了子節點之間的遍歷順序。之後,借助棧的特性,實現了上述兩條規則,即實現了樹的遍歷。
演算法的優點是將遍歷順序與演算法邏輯之間的分離,於是使用哪一種遍歷順序,不影響演算法本身的邏輯。換一句話說,不管是哪一種遍歷順序,**的整體框架是一樣的,只需稍微改變跟順序相關的幾句**,就ok了。除此之外,很容易推廣到多叉樹。
演算法的缺點嘛,對於每個節點都需要入棧兩次,同時對於每個節點都需要分配乙個標誌位,但是我覺得瑕不掩瑜。
二叉樹的非遞迴前序遍歷
二叉樹的非遞迴前序遍歷,需要借助棧 棧又是由順序表實現的 順序表的實現 首先我們需要乙個二叉樹 通過前序遍歷的陣列 abd gi j ce hk f 構建二叉樹 之前我們已經有二叉樹遞迴版的前序,中序,後序遍歷 通過前序遍歷的陣列 abd gi j ce hk f 構建二叉樹,並得到二叉樹的前序遍歷...
二叉樹的前序非遞迴遍歷
include 二叉樹的先序遞迴遍歷 include include 假定利用陣列a n 順序儲存乙個棧,用top 表示棧頂指標,top 1表示棧空,已知棧未滿,當元素x進棧時的操作為 x b.a top x c.a top x d.a top x top是先 1再運算,所以是從0開始的 而top ...
樹 二叉樹的前序遍歷 非遞迴寫法
今天碰到了這道題,寫一下怎麼實現樹的前序遍歷而不使用遞迴。在我之前的部落格中曾經寫過,雖然遞迴和非遞迴有著緊密的聯絡 比如思想上 但是兩者在編寫上依然有很大差別。主要體現在遞迴可以使用所謂整體法,而如果使用非遞迴的話用整體法很難想。如果使用非遞迴的話怎麼搞呢?我們只能先通過模擬,摸清大概是怎麼做的,...