七、二叉樹(四)、二叉樹的遍歷

數(shù)據(jù)結構目錄

1.概覽

二叉樹的遍歷(traversing binary tree)是指從根節(jié)點出發(fā),按照某種次序依次訪問二叉樹中所有結點,使得每個節(jié)點被訪問依次且僅被訪問一次。
注意:

  1. 從根節(jié)點出發(fā),并不一定是從根節(jié)點開始遍歷,只是從根節(jié)點開始邏輯
  2. 次序表示有一定的規(guī)則
  3. 訪問表示可以得到這個結點的信息

二叉樹的遍歷次序不同于線性結構,線性結構最多也就是分為順序、循環(huán)、雙向等簡單的遍歷方式。

二叉樹的遍歷方式可以很多,如果我們限制了從左到右的習慣方式,那么主要就分為以下四種:

  • 前序遍歷
  • 中序遍歷
  • 后序遍歷
  • 層序遍歷

2.前序遍歷

定義:若二叉樹為空,則空操作返回,否則先訪問根節(jié)點,然后前序遍歷左子樹,再前序遍歷右子樹

二叉樹前序遍歷的順序圖示

如圖所示,先訪問根節(jié)點A,然后訪問A的左子樹B,再訪問B的左子樹D,再訪問D的左子樹H,由于H是終端結點,則再訪問D的右子樹I,訪問完了B的左子樹,再訪問B的右子樹,剩下的類推可得到

總結:前序遍歷中,遵循根節(jié)點->左子樹->右子樹的順序來訪問結點

3. 中序遍歷

定義:若二叉樹為空,則空操作返回,否則從根節(jié)點開始(注意并不是先訪問根節(jié)點),中序遍歷根節(jié)點的左子樹,然后是訪問根節(jié)點,最后中序遍歷右子樹。

二叉樹的中序遍歷的順序圖示

總結:中序遍歷遵循左子樹->根節(jié)點->右子樹的順序來訪問結點

4.后序遍歷

定義:若樹為空,則空操作返回,否則從左到右先葉子后結點的方式遍歷訪問左右子樹,最后訪問根節(jié)點

二叉樹的后序遍歷的順序

總結:后序遍歷遵循左子樹->右子樹->根節(jié)點的順序來訪問結點

5.層序遍歷

定義:若樹為空,則空操作返回,否則從樹的第一層,也就是根節(jié)點開始訪問,從上而下逐層遍歷,在同一層中,按從左到右的順序對結點逐個訪問


二叉樹的層序遍歷順序圖示

總結:層序遍歷遵循層級遞增,同層級中從左至右的順序訪問結點

6.算法:使用前序遍歷,建立二叉樹并進行遍歷,輸出各個結點的層次

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;

typedef char ElemType;

typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//創(chuàng)建一棵二叉樹,約定用戶遵照前序遍歷的方式輸入數(shù)據(jù)
Status createBiTree(BiTree *T){
    //接收輸入
    ElemType c = 0;
    scanf("%c",&c);
    
    if ( c == ' ') {
        //輸入空格便是結束這棵子樹
        *T = NULL;
        return ERROR;
    } else {
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T) -> data = c;
        //遞歸創(chuàng)建
        //創(chuàng)建左子樹
        createBiTree(&((*T)->lchild));
        //創(chuàng)建右子樹
        createBiTree(&((*T)->rchild));
        
        return OK;
    }
}

//訪問二叉樹結點的具體操作
void visit(ElemType c,int level){
    printf("data:%c,level:%d",c,level);
    
}
//前序遍歷
Status preOrderTraverse(BiTree T,int level){
    if ( !T ) {
        return ERROR;
    }
    visit(T->data, level);
    //遞歸遍歷
    preOrderTraverse(T->lchild, level+1);
    preOrderTraverse(T->rchild, level+1);
    
    
    return OK;
}
int main(){
    int level = 1;
    BiTree T = NULL;
    createBiTree(&T);
    preOrderTraverse(T, level);
    
    return 0;
}

7.二叉樹的前序、中序、后序遍歷的遞歸與非遞歸實現(xiàn)

二叉樹遍歷的非遞歸實現(xiàn)需要借助棧來實現(xiàn)(由于棧LIFO的特性),我們先建立一個樹的結點棧,如下所示:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;

typedef char ElemType;

typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct BiTreeStack{
    BiTNode *base;//棧底
    BiTNode *top;//棧頂
    int stackSize;
}BiTreeStack;

Status initStack(BiTreeStack *s){
    s->base = (BiTNode *)malloc(sizeof(BiTNode) * STACK_INIT_SIZE);
    if (!s->base) {
        return ERROR;
    }
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
    return OK;
}

Status Push(BiTreeStack *s,BiTNode node){
    if (s->top - s->base >= s->stackSize) {
        s->base = (BiTNode *)realloc(s->base, sizeof(BiTNode)*(s->stackSize + STACKINCREMENT));
        if (!s->base) {
            return ERROR;
        }
        s->top = s->base + s->stackSize;
        s->stackSize += STACKINCREMENT;
    }
    *(s->top) = node;
    (s->top)++;
    return OK;
}
Status Pop(BiTreeStack *s,BiTNode *node){
    if (s->top - s->base == 0) {
        return ERROR;
    }
    *node = *(--(s->top));
    return OK;
}
int StackLength(BiTreeStack *s){
    return (int)(s->top - s->base);
}
void visitNode(BiTree node){
    printf("data:%c",node->data);
}

前序遍歷

前序遍歷的遞歸實現(xiàn)比較簡單,遵循根節(jié)點-左子樹-右子樹的順序即可

//遞歸
Status PreorderTraversalRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    visitNode(T);
    PreorderTraversalRecursive(T->lchild);
    PreorderTraversalRecursive(T->rchild);
    return OK;
}

非遞歸實現(xiàn)我們需要創(chuàng)建一個棧,先把二叉樹的根節(jié)點壓入棧,訪問后,再依次壓入右結點,再壓入左結點,這樣訪問的時候就遵循了根節(jié)點-左子樹-右子樹的順序

//非遞歸
Status PreorderTraversalNotRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    BiTNode node = *T;
    BiTreeStack s;
    BiTreeStack *stack = NULL;
    initStack(&s);
    stack = &s;
    Push(stack, node);
    
    while (StackLength(stack) != 0) {
        Pop(stack, &node);
        visitNode(&node);
        //先把右子樹根節(jié)點壓入棧
        if (node.rchild) {
            Push(stack, *(node.rchild));
        }
        //再把左子樹根節(jié)點壓入棧
        if (node.lchild) {
            Push(stack, *(node.lchild));
        }
    }
    return OK;
}

中序遍歷

中序遍歷的遞歸實現(xiàn)比較簡單,遵循左子樹-根節(jié)點-右子樹的順序即可

//遞歸
Status InorderTraversalRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    InorderTraversalRecursive(T->lchild);
    visitNode(T);
    InorderTraversalRecursive(T->rchild);
    
    return OK;
}

中序遍歷的非遞歸實現(xiàn)稍微麻煩一點,我們需要先從根節(jié)點出發(fā),逐個將左子樹壓入棧,由于我們知道葉子結點是沒有子樹的,那么我每次每次訪問的就當做是根節(jié)點,我們判斷它是否有右子樹,有右子樹的話,就重復之前步驟,將左子樹依次加入到棧中,這樣也就遵循了中序遍歷左子樹-根節(jié)點-右子樹的順序

//非遞歸
Status InorderTraversalNotRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    BiTNode *p = T;
    BiTreeStack s;
    BiTreeStack *stack = NULL;
    initStack(&s);
    stack = &s;
    //將左子樹都壓入棧
    while (p) {
        Push(stack, *p);
        p = p->lchild;
    }
    //開始遍歷
    while (StackLength(stack)) {
        //每一個出棧的結點我們都當做根節(jié)點
        Pop(stack, p);
        visitNode(p);
        //判斷有無右子樹
        if (p->rchild) {
            p = p->rchild;
            Push(stack, *p);
            while (p->lchild) {
                p = p->lchild;
                Push(stack, *p);
            }
        }
    }
    
    return OK;
}

后序遍歷

后序遍歷的遞歸實現(xiàn)也比較簡單,遵循左子樹-右子樹-根節(jié)點的順序即可

//遞歸
Status PostorderTraversalRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    PostorderTraversalRecursive(T->lchild);
    PostorderTraversalRecursive(T->rchild);
    visitNode(T);
    
    return OK;
}

后序遍歷的非遞歸實現(xiàn)看上去是最麻煩的,不過我們仍然可以用按照后序遍歷左子樹-右子樹-根節(jié)點的思路來進行思考:

  1. 對于每一個結點,如果它是非葉子結點,且它的左孩子和右孩子都不是上一次訪問的結點的情況下,循環(huán)將它的左子樹壓入棧,這樣就確定了訪問的順序
  2. 將棧頂結點推出,判斷結點是否有右孩子且右孩子不是之前訪問的結點的情況下,將右孩子壓入棧
  3. 棧頂結點推出,不符合2的條件下,訪問結點,并存儲新訪問結點的指針,然后將出棧新節(jié)點有用于比較
//非遞歸
Status PostorderTraversalNotRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    BiTNode *p = T;
    //記錄上一次訪問的結點
    BiTNode *pre = T;
    BiTreeStack s;
    BiTreeStack *stack = NULL;
    initStack(&s);
    stack = &s;
    
    while(p || StackLength(stack)){
        if(p != NULL && pre != p->lchild && pre != p->rchild){    //結點不為空且左孩子和右孩子都不是上一次訪問的,入棧左子樹
            Push(stack, *p);
            p = p->lchild;
        }
        else{
            Pop(stack, p);
            if(p->rchild != NULL && pre != p->rchild){    //右子樹不為空且右孩子沒有訪問過,入棧右子樹結點
                Push(stack, *p);
                p = p->rchild;
                Push(stack, *p);
            }
            else{
                //訪問到最后的右子樹的結點后,退棧
                visitNode(p);
                pre = p;
                Pop(stack, p);
            }
        }
    }
    return OK;
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380