樹:二叉樹的集中遍歷方法(先序,中序,後序遍歷,線索二叉樹)

一:二叉樹的幾種遍歷方法

這裏寫圖片描述

1:先序遍歷

根→左→右
先訪問根節點,再遍歷左子樹,最後遍歷右子樹;並且在遍歷左右子樹時,仍需先訪問根節點,然後遍歷左子樹,最後遍歷右子樹。
比如上圖,先序遍歷的輸出如下 : - + a * b - c d / e f

根據上面的思想,很容易用遞歸的形式寫出先序遍歷的代碼:

//先序遍歷  
Status PreOrderTraverse(BiTree T , Status(*visit)(TElemType)){
    if(T){
        if(visit( T->data )){//打印T->data
            if(PreOrderTraverse( T->lchild , visit )){
                if(PreOrderTraverse( T->rchild , visit )){
                    return OK;
                }
            }
        }
    }else return OK;
}

2,中序遍歷

左→根→右
先遍歷左子樹、然後訪問根節點,最後遍歷右子樹;並且在遍歷左右子樹的時候。仍然是先遍歷左子樹,然後訪問根節點,最後遍歷右子樹。
比如上圖,中序遍歷的輸出如下 : a + b * c - d - e / f

3,後序遍歷

左→右→根
先遍歷左子樹,然後遍歷右子樹,最後訪問根節點;同樣,在遍歷左右子樹的時候同樣要先遍歷左子樹,然後遍歷右子樹,最後訪問根節點。
比如上圖,後序遍歷的輸出如下 : a b c d - * + e f / -

例題:

知道後序DABEC,中序DEBAC,求二叉樹圖
由後序最後一個字母知:整個樹的開始結點爲C;
由中序C的位置知:C前面的爲結點C的左子樹;C後面的爲結點C的右子樹;
所以經過第一次推理,C爲根結點,DEBA爲其左子樹;
然後去掉C,考慮下面的左子樹;
後序DABE 中序DEBA
由後序最後一個字母知:整個左子樹的開始結點爲E;
由中序E的位置知:E前面的爲結點E的左子樹;E後面的爲結點E的右子樹;
所以經過第一次推理,E爲開始結點,DE的左結點.BA爲E的右結點.
然後去掉DE,考慮下面E的右子樹;
後序AB 中序BA
易知:B爲根結點,A爲其右結點.
所以整個樹爲:C(E(D,B(,A)));
先序:CEDBA

二:使用代碼實現遍歷

這裏寫圖片描述

1,先創建二叉樹

#include <iostream>
using namespace std;
char array[] = "ABD##E##C#F##";  
int i(0);

typedef struct BTree{
    char data;
    struct BTree *left,*right;

};
//創建子節點(遞歸)
void CerateNode(BTree *&p){//注意*&p在c中表示爲**p,是指針地址
    i++;
    if(strlen(array)>i&&array[i]!='#'){
        BTree* node = (BTree*)malloc(sizeof(BTree));
        node->left = 0;
        node->right = 0;
        node->data = array[i];
        p = node;//指針地址指向節點
        CerateNode(node->left);//左
        CerateNode(node->right);//右
    }else{
        return;
    }


}
//創建根節點
BTree* CreateRoot(){
    BTree* node = (BTree*)malloc(sizeof(BTree));
    node->left = 0;
    node->right = 0;
    node->data = array[0];
    return node;
}

//主函數
void main(void){
    BTree* head = CreateRoot();//創建根節點
    CerateNode( head->left);//添加左子樹
    CerateNode(head->right);//添加右子樹
    cout<<endl<<"先序:";
    PreOrderTravse(head,0);
    cout<<endl<<"中序:";
    PreOrderTravse(head,1);
    cout<<endl<<"後序:";
    PreOrderTravse(head,2);
    cout<<endl;
    system("pause");
}

2,遞歸方便遍歷

//遍歷i=0時爲先序,i=1是爲中序,i=2時爲後序
void PreOrderTravse(BTree* &t,int i){
    if(t){
        if(i==0)cout<<t->data;//先序
        if(!t->left==0)PreOrderTravse(t->left,i);
        if(i==1)cout<<t->data;//中序
        if(!t->right==0)PreOrderTravse(t->right,i);
        if(i==2)cout<<t->data;//後序
    }
}

3,非遞歸方法遍歷

後序遍歷二叉樹(非遞歸)思想

1.對於一棵樹t,如果t非空,首先應該進入t的左子樹訪問,此時由於t的右子樹及根結點尚未訪問,
2. 因此必須將t保存起來,放入棧中,以便訪問完左子樹後,從棧中取出t,進行其右子樹及根結點的訪問。
3. 這裏值得注意的是,當一個元素位於棧頂即將處理時,其左子樹的訪問一定已經完成,
4. 如果其右子樹尚未遍歷,接下來應該進入其右子樹的訪問,而此時該棧頂元素是不能出棧的,
5. 因爲其根結點還未被訪問;只有等到其右子樹也訪問完成後,該棧頂元素才能出棧,並輸其根結點的值。
6. 因此,在二叉樹後序遍歷的過程中,必須使用標記數組,其每個元素取值爲0或1,
7. 用於標識棧中每個元素的狀態。當一個元素剛進棧時,其對應的tag值置0;當它第一次位於棧頂即將
8. 被處理時,其tag值爲0,意味着應該訪問其右子樹,於是將其右子樹作爲當前處理的對象,
9. 此時該棧頂元素仍應該保留在棧中,並將其對應的tag值改爲1;當其右子樹訪問完成後,
10. 該元素又一次位於棧頂,而此時其tag值爲1,意味着應該訪問其根結點,並將其出棧。
11.在整個二叉樹後序遍歷的過程中,程序要做的工作始終分成兩個部分:當前正在處理的樹(子樹)
12.和保存在棧中待處理的部分。只有這兩部分的工作均完成後,程序方能結束。

BTree *stack[30] = {0}; //用一個靜態數組模擬棧
int ntag[30] = {0};//標記數組標記每個元素的左右子樹是否都已經被訪問過了  
int top = -1;//初始狀態下Top=-1表示棧空  

//非遞歸的先序,中序遍歷
void PreOrderTravse2(BTree* t,int i){
    int top = -1;
    BTree *p = t;
    while(p!=0||top!=-1){//當前指針不空或棧不空  
        while (p){//當前指針不空  
            if(i==0)cout<<p->data;
            stack[++top] = p;//將當前指針入棧  
            p = p->left;
        }
        if(top!=-1){//棧不空  
            p = stack[top--];//獲取棧頂指針且棧頂指針出棧
            if(i==1)cout<<p->data;
            p = p->right;
        }
    }
}
//後序遍歷
void hou(BTree* t){
    top = -1;
    BTree *p = t;
    while(p!=0||top!=-1){//當前指針不空或棧不空  
        while (p){//當前指針不空  
            stack[++top] = p;//將當前指針入棧  
            ntag[top] = 0;//表示第一次位於棧頂  
            p = p->left;
        }
        //棧不空且棧頂指針指向的結點被標記爲1即表示給結點的左右子樹都已經被訪問過了,第二次位於棧頂  
        while(top!=-1&&ntag[top]==1){
            BTree *c = stack[top--];//獲取棧頂指針且棧頂指針出棧
            cout<<c->data;
        }
        if(top!=-1){//棧不空  此時標記爲0,意味着應該訪問其右子樹   
            p = stack[top];//取到棧頂元素,注意此時不能出棧
            ntag[top] = 1;
            p = p->right;
        }
    }
}
發佈了47 篇原創文章 · 獲贊 26 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章