二叉樹的遞迴和非遞迴方式的三種遍歷
二叉樹的三種遍歷方式,
前序遍歷
,中序遍歷
,後序遍歷
,中的前中後都是指的是根節點的訪問順序,這三種遍歷方式的概念在這裡就不多說了,太普遍了!
二叉樹的建立
我們這裡以前序遍歷為例:
我們先定義好結構體
struct Tree{
Tree* lson;
Tree* rson;
int data;
};
Tree* T;
下面是前序建立二叉樹:
void createBtiTree(Tree* &tree)
{
int data;
cin>>data;
if(data==-1) {
tree= NULL;
}else {
tree = new Tree();
tree->data = data;
createBtiTree(tree->lson);
createBtiTree(tree->rson);
}
}
二叉樹的遞迴方式遍歷
當我們覺得構造遞迴困難的時候(不知道在這裡用構造這個詞對不對),我們就可以把遞迴想象成為一棵樹,這樣就容易構造一些!
遞迴的三種遍歷方式比較基礎,在這裡就不過多進行贅述,直接給出程式碼!如果讀者有困難的話,可以試著在紙上描述一下,畢竟遞迴是要深入的東西!
前序遍歷
void preOrderRecur(Tree* tree)
{
if(tree==NULL) return ;
cout<<tree->data<<" ";
preOrderRecur(tree->lson);
preOrderRecur(tree->rson);
}
中序遍歷
void inOrderRecur(Tree* tree)
{
if(tree==NULL) return ;
inOrderRecur(tree->lson);
cout<< tree->data<<" ";
inOrderRecur(tree->rson);
}
後序遍歷
void posOrderRecur(Tree* tree)
{
if(tree==NULL) return ;
posOrderRecur(tree->lson);
posOrderRecur(tree->rson);
cout<<tree->data<<" ";
}
非遞迴形式的二叉樹的三種遍歷方式
用遞迴解決的問題都能用非遞迴的方式解決。因為遞迴是使用函式棧來儲存資訊的,如果自己用自己申請的資料結構來代替函式棧就能實現相同的功能!
前序遍歷
前序遍歷也是最簡單的一種,分為下面三個步驟:
1. 申請一個棧,將頭結點壓入棧
2. 彈出棧頂指標,記作:cur
,如果這個這個指標有右孩子,將右孩子入棧,如果有左孩子,將左孩子入棧;
3. 不斷重複過程2,直到棧為空,結束程式!
實現程式碼
void PreOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
sta.push(tree);
while(!sta.empty()) {
Tree* cur = sta.top();
sta.pop();
cout<<cur->data<<" ";
if(cur->rson!=NULL) sta.push(cur->rson);
if(cur->lson!=NULL) sta.push(cur->lson);
}
}
中序遍歷
- 申請一個棧,頭結點為開始節點(當前節點)
- 如果當前節點不為空,那麼將左節點壓棧,即做
tree=tree->lson
操作,如果當前節點為空的時候列印棧頂元素,並且出棧,將 當前節點變為棧頂元素的右節點也就是做tree = cur->rson
(中序遍歷中,棧主要儲存的是父節點元素) - 不斷重複步驟2直到棧空,結束程式!
實現程式碼
void InOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
while(!sta.empty() || tree!=NULL) {
if(tree==NULL) {
Tree* cur = sta.top();
sta.pop();
cout<<cur->data<<" ";
tree=cur->rson;
} else {
sta.push(tree);
tree=tree->lson;
}
}
}
後序遍歷
後序遍歷用棧實現起來相對前面兩種遍歷是難實現一些,這這裡給出兩個方法:第一種方法是用兩個棧來實現的(比較好理解),第二種是用一個棧來實現的!
兩個棧的方法實現
先說兩個棧來實現的方法,第一個棧儲存的是根節點元素,第二棧是儲存輸出的元素!過程如下:
- 申請一個棧,將根節點入棧
- 如果棧不為空,彈出第一個棧的棧頂元素記做
cur
,將第一個棧頂元素出棧,然後將cur
壓入第二個棧。如果cur
有左孩子將左孩子加入第一個棧,如果有右孩子將右孩子加入第一個棧 - 不斷的重複步驟2,直到第一個棧為空,列印第二個棧,結束程式!
實現程式碼
void PosOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
while(!Sta.empty()) Sta.pop();
sta.push(tree);
while(!sta.empty()) {
Tree* cur = sta.top();
sta.pop();Sta.push(cur);
if(cur->lson!=NULL) sta.push(cur->lson);
if(cur->rson!=NULL) sta.push(cur->rson);
}
while(!Sta.empty()) {
cout<<Sta.top()->data<<" ";
Sta.pop();
}
}
一個棧的實現
我們用cur
表示棧頂元素,h
表示的是最近棧的元素,初始化時h為頭結點。演算法流程如下:
1. 申請一個棧 ,將頭結點壓棧,初始化h
變數,
2. 如果棧不為空,cur
賦為棧頂元素!
—- 1.如果cur
的左孩子不為NULL
並且h
不等於cur
的左孩子也不等於cur
的右孩子那麼就將左孩子入棧。(如果最近h等於當前節點的左孩子,就說明左子樹已經列印完了,否則就代表還沒有列印過,就應該將左孩子或者右孩子入棧)
—- 2.在1條件不成立的條件下,並且cur
的右孩子不等於h
並且不為空,就說明右子樹還沒有處理過,這個時候就應該將cur
的右孩子入棧!
—- 3.如果前倆個條件都不成立,就說明cur
的左子樹和右子樹已經列印完畢了,或者當前節點為葉子節點,此時就應該將棧頂元素出棧了,並且令h
=cur
3. 一直重複步驟2直到棧為空,結束程式
實現程式碼
void TPosOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
sta.push(tree);
Tree* c=tree;
while(!sta.empty()) {
Tree* cur = sta.top();
if(cur->lson!=NULL && cur->lson!=c && cur->rson!=c){
sta.push(cur->lson);
} else if(cur->rson!=NULL && c!=cur->rson) {
sta.push(cur->rson);
} else{
sta.pop();
c=cur;
cout<<cur->data<<" ";
}
}
}