二叉樹的三種遍歷方式的遞迴實現和非遞迴實現

2021-07-03 13:44:14 字數 3960 閱讀 8564

二叉樹的非遞迴遍歷

二叉樹是一種非常重要的資料結構,很多其它資料結構都是基於二叉樹的基礎演變而來的。對於二叉樹,有前序、中序以及後序三種遍歷方法。因為樹的定義本身就是遞迴定義,因此採用遞迴的方法去實現樹的三種遍歷不僅容易理解而且**很簡潔。而對於樹的遍歷若採用非遞迴的方法,就要採用棧去模擬實現。在三種遍歷中,前序和中序遍歷的非遞迴演算法都很容易實現,非遞迴後序遍歷實現起來相對來說要難一點。

一.前序遍歷

前序遍歷按照「根結點-左孩子-右孩子」的順序進行訪問。

1.遞迴實現

void preorder1(bintree *root)     //遞迴前序遍歷 

}

2.非遞迴實現

根據前序遍歷訪問的順序,優先訪問根結點,然後再分別訪問左孩子和右孩子。即對於任一結點,其可看做是根結點,因此可以直接訪問,訪問完之後,若其左孩子不為空,按相同規則訪問它的左子樹;當訪問其左子樹時,再訪問它的右子樹。因此其處理過程如下:

對於任一結點p:

1)訪問結點p,並將結點p入棧;

2)判斷結點p的左孩子是否為空,若為空,則取棧頂結點並進行出棧操作,並將棧頂結點的右孩子置為當前的結點p,迴圈至1);若不為空,則將p的左孩子置為當前的結點p;

3)直到p為null並且棧為空,則遍歷結束。

void preorder2(bintree *root)     //非遞迴前序遍歷 

if(!s.empty())

}}

二.中序遍歷

中序遍歷按照「左孩子-根結點-右孩子」的順序進行訪問。

1.遞迴實現

void inorder1(bintree *root)      //遞迴中序遍歷

}

2.非遞迴實現

根據中序遍歷的順序,對於任一結點,優先訪問其左孩子,而左孩子結點又可以看做一根結點,然後繼續訪問其左孩子結點,直到遇到左孩子結點為空的結點才進行訪問,然後按相同的規則訪問其右子樹。因此其處理過程如下:
對於任一結點p,

1)若其左孩子不為空,則將p入棧並將p的左孩子置為當前的p,然後對當前結點p再進行相同的處理;

2)若其左孩子為空,則取棧頂元素並進行出棧操作,訪問該棧頂結點,然後將當前的p置為棧頂結點的右孩子;

3)直到p為null並且棧為空則遍歷結束

void inorder2(bintree *root)      //非遞迴中序遍歷

if(!s.empty())

}

}

三.後序遍歷

後序遍歷按照「左孩子-右孩子-根結點」的順序進行訪問。

1.遞迴實現

void postorder1(bintree *root)    //遞迴後序遍歷

}

2.非遞迴實現

後序遍歷的非遞迴實現是三種遍歷方式中最難的一種。因為在後序遍歷中,要保證左孩子和右孩子都已被訪問並且左孩子在右孩子前訪問才能訪問根結點,這就為流程的控制帶來了難題。下面介紹兩種思路。

第一種思路:對於任一結點p,將其入棧,然後沿其左子樹一直往下搜尋,直到搜尋到沒有左孩子的結點,此時該結點出現在棧頂,但是此時不能將其出棧並訪問,因此其右孩子還為被訪問。所以接下來按照相同的規則對其右子樹進行相同的處理,當訪問完其右孩子時,該結點又出現在棧頂,此時可以將其出棧並訪問。這樣就保證了正確的訪問順序。可以看出,在這個過程中,每個結點都兩次出現在棧頂,只有在第二次出現在棧頂時,才能訪問它。因此需要多設定乙個變數標識該結點是否是第一次出現在棧頂。

void postorder2(bintree *root)    //非遞迴後序遍歷

if(!s.empty())

else

//第二次出現在棧頂

}}

}

第二種思路:要保證根結點在左孩子和右孩子訪問之後才能訪問,因此對於任一結點p,先將其入棧。如果p不存在左孩子和右孩子,則可以直接訪問它;或者p存在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。若非上述兩種情況,則將p的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。

void postorder3(bintree *root)     //非遞迴後序遍歷

else

}

}

四.整個程式完整的**

/*二叉樹的遍歷* 2011.8.25*/

#include

#include

#include

using

namespace

std;

typedef

struct node

bintree;

typedef

struct node1

btnode;

void creatbintree(char *s,bintree *&root) //建立二叉樹,s為形如a(b,c(d,e))形式的字串

else

if(s[i]==',')

else

if(s[i]==')')

else

if(isalpha(s[i]))

i++;

}

}void display(bintree *root) //顯示樹形結構

if(root->rchild!=null)

}}void preorder1(bintree *root) //遞迴前序遍歷

}void inorder1(bintree *root) //遞迴中序遍歷

} void postorder1(bintree *root) //遞迴後序遍歷

} void preorder2(bintree *root) //非遞迴前序遍歷

if(!s.empty())

}}void inorder2(bintree *root) //非遞迴中序遍歷

if(!s.empty())

}

} void postorder2(bintree *root) //非遞迴後序遍歷

if(!s.empty())

else

//第二次出現在棧頂

}}

} void postorder3(bintree *root) //非遞迴後序遍歷

else

}

}int main(int argc, char *argv)

其中針對各種遍歷方式的非遞迴實現(遞迴實現比較簡單易懂),說一點便於我自己理解的東西:

1.無論是前序遍歷、後續遍歷、中序遍歷,首先需要做的都是從根節點開始將節點及其左子節點一直壓入棧中;

2.但是對於不同的遍歷方式,壓入該節點及其左子節點的方式也是不同的:

(1)對於前序遍歷,首先是要輸出根節點,對於左子樹同樣是輸出該子樹的根節點;所以在將壓棧的過程中,要先輸出它的值,然後再將該值壓棧;

(2)對於中序遍歷,輸出方式為:」左兒子-根-右兒子」的方式來進行的,所以要先輸入節點,一直輸入到某一節點沒有左兒子之後,從棧中彈出元素,那麼最先彈出的一定是左節點,這符合」左兒子-根-右兒子」的遍歷方式,因此這個時候才對該點進行輸出。在彈出的時候再找它的右兒子,完美的完成了」左兒子-根-右兒子」的遍歷方式;

(3)對於後續遍歷,起始加入bool常量就是為了改變一下輸出的順序,本來在輸出完左兒子之後就要輸出父節點,但是由於測試了一下bool變數的值,使得父節點沒有機會出棧,而之後確實右兒子壓棧,這樣就完成了右兒子一定會在父節點之前輸出,從而完成了後續遍歷的過程;

二叉樹三種遍歷方式的非遞迴實現

說明 include include include include include 棧using namespace std define n 100 最大節點個數 typedef struct mybinarytree bt bt createbt char pre,char in,int k ...

二叉樹的三種遞迴遍歷實現

那麼爸爸 媽媽 就是樹的根,倆孩子就是這棵樹的葉子,而此時這種,孩子只有唯一父輩 爸爸媽媽看作乙個整體 父輩或者沒有孩子,或者有且最多有兩個孩子的樹就叫做二叉樹,可以參考下圖 就這樣這個家族子子輩輩形成乙個越來越高的二叉樹。注意看它是倒過來的 那麼這個時候故事來了 有一家的的家譜非常奇特,它從第一頁...

二叉樹三種遍歷方式非遞迴實現

1.中序遍歷 應為輸出的子樹根的關鍵字位於其左子樹關鍵字值和右子樹的關鍵字值之間 同理,先序後序遍歷。1.先序遍歷非遞迴演算法 define maxsize 100 typedef struct sqstack void preorderunrec bitree t endwhile if stac...