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

數(shù)據(jù)結(jié)構(gòu)目錄

1.概覽

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

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

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

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

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

2.前序遍歷

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

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

如圖所示瑞信,先訪問根節(jié)點(diǎn)A厉颤,然后訪問A的左子樹B,再訪問B的左子樹D凡简,再訪問D的左子樹H逼友,由于H是終端結(jié)點(diǎn),則再訪問D的右子樹I秤涩,訪問完了B的左子樹帜乞,再訪問B的右子樹,剩下的類推可得到

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

3. 中序遍歷

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

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

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

4.后序遍歷

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

二叉樹的后序遍歷的順序

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

5.層序遍歷

定義:若樹為空宝恶,則空操作返回符隙,否則從樹的第一層,也就是根節(jié)點(diǎn)開始訪問垫毙,從上而下逐層遍歷霹疫,在同一層中,按從左到右的順序?qū)Y(jié)點(diǎn)逐個(gè)訪問


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

總結(jié):層序遍歷遵循層級(jí)遞增综芥,同層級(jí)中從左至右的順序訪問結(jié)點(diǎn)

6.算法:使用前序遍歷更米,建立二叉樹并進(jìn)行遍歷,輸出各個(gè)結(jié)點(diǎn)的層次

#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 == ' ') {
        //輸入空格便是結(jié)束這棵子樹
        *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;
    }
}

//訪問二叉樹結(jié)點(diǎn)的具體操作
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.二叉樹的前序征峦、中序、后序遍歷的遞歸與非遞歸實(shí)現(xiàn)

二叉樹遍歷的非遞歸實(shí)現(xiàn)需要借助棧來實(shí)現(xiàn)(由于棧LIFO的特性)消请,我們先建立一個(gè)樹的結(jié)點(diǎn)棧栏笆,如下所示:

#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);
}

前序遍歷

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

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

非遞歸實(shí)現(xiàn)我們需要?jiǎng)?chuàng)建一個(gè)棧臊泰,先把二叉樹的根節(jié)點(diǎn)壓入棧蛉加,訪問后,再依次壓入右結(jié)點(diǎn)缸逃,再壓入左結(jié)點(diǎn)针饥,這樣訪問的時(shí)候就遵循了根節(jié)點(diǎn)-左子樹-右子樹的順序

//非遞歸
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é)點(diǎn)壓入棧
        if (node.rchild) {
            Push(stack, *(node.rchild));
        }
        //再把左子樹根節(jié)點(diǎn)壓入棧
        if (node.lchild) {
            Push(stack, *(node.lchild));
        }
    }
    return OK;
}

中序遍歷

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

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

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

//非遞歸
Status InorderTraversalNotRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    BiTNode *p = T;
    BiTreeStack s;
    BiTreeStack *stack = NULL;
    initStack(&s);
    stack = &s;
    //將左子樹都?jí)喝霔?    while (p) {
        Push(stack, *p);
        p = p->lchild;
    }
    //開始遍歷
    while (StackLength(stack)) {
        //每一個(gè)出棧的結(jié)點(diǎn)我們都當(dāng)做根節(jié)點(diǎn)
        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;
}

后序遍歷

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

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

后序遍歷的非遞歸實(shí)現(xiàn)看上去是最麻煩的惠啄,不過我們?nèi)匀豢梢杂冒凑蘸笮虮闅v左子樹-右子樹-根節(jié)點(diǎn)的思路來進(jìn)行思考:

  1. 對(duì)于每一個(gè)結(jié)點(diǎn),如果它是非葉子結(jié)點(diǎn)任内,且它的左孩子和右孩子都不是上一次訪問的結(jié)點(diǎn)的情況下撵渡,循環(huán)將它的左子樹壓入棧,這樣就確定了訪問的順序
  2. 將棧頂結(jié)點(diǎn)推出族奢,判斷結(jié)點(diǎn)是否有右孩子且右孩子不是之前訪問的結(jié)點(diǎn)的情況下姥闭,將右孩子壓入棧
  3. 棧頂結(jié)點(diǎn)推出,不符合2的條件下越走,訪問結(jié)點(diǎn)棚品,并存儲(chǔ)新訪問結(jié)點(diǎn)的指針,然后將出棧新節(jié)點(diǎn)有用于比較
//非遞歸
Status PostorderTraversalNotRecursive(BiTree T){
    if (!T) {
        return ERROR;
    }
    BiTNode *p = T;
    //記錄上一次訪問的結(jié)點(diǎn)
    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){    //結(jié)點(diǎn)不為空且左孩子和右孩子都不是上一次訪問的,入棧左子樹
            Push(stack, *p);
            p = p->lchild;
        }
        else{
            Pop(stack, p);
            if(p->rchild != NULL && pre != p->rchild){    //右子樹不為空且右孩子沒有訪問過廊敌,入棧右子樹結(jié)點(diǎn)
                Push(stack, *p);
                p = p->rchild;
                Push(stack, *p);
            }
            else{
                //訪問到最后的右子樹的結(jié)點(diǎn)后铜跑,退棧
                visitNode(p);
                pre = p;
                Pop(stack, p);
            }
        }
    }
    return OK;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骡澈,隨后出現(xiàn)的幾起案子锅纺,更是在濱河造成了極大的恐慌,老刑警劉巖肋殴,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囤锉,死亡現(xiàn)場(chǎng)離奇詭異坦弟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)官地,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門酿傍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人驱入,你說我怎么就攤上這事赤炒。” “怎么了亏较?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵莺褒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我雪情,道長(zhǎng)遵岩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任旺罢,我火速辦了婚禮旷余,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扁达。我一直安慰自己正卧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布跪解。 她就那樣靜靜地躺著炉旷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叉讥。 梳的紋絲不亂的頭發(fā)上窘行,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音图仓,去河邊找鬼罐盔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛救崔,可吹牛的內(nèi)容都是我干的惶看。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼六孵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼纬黎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起劫窒,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤本今,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冠息,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挪凑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铐达。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岖赋。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓮孙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情选脊,我是刑警寧澤杭抠,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站恳啥,受9級(jí)特大地震影響偏灿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钝的,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一翁垂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硝桩,春花似錦沿猜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衙伶,卻和暖如春祈坠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背矢劲。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工赦拘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芬沉。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓躺同,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親花嘶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笋籽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355