二叉樹前序遍歷、中序遍歷和后序遍歷及C語言非遞歸實現


遞歸算法底層的實現使用的是棧存儲結構,所以可以直接使用棧寫出相應的非遞歸算法。

先序遍歷的非遞歸算法

從樹的根結點出發,遍歷左孩子的同時,先將每個結點的右孩子壓棧。當遇到結點沒有左孩子的時候,取棧頂的右孩子。重復以上過程。

實現代碼函數:
//先序遍歷非遞歸算法
void PreOrderTraverse(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    push(a, Tree);//根結點進棧
    while (top!=-1) {
        p=getTop(a);//取棧頂元素
        pop();//彈棧
        while (p) {
            displayElem(p);//調用結點的操作函數
            //如果該結點有右孩子,右孩子進棧
            if (p->rchild) {
                push(a,p->rchild);
            }
            p=p->lchild;//一直指向根結點最后一個左孩子
        }
    }
}

中序遍歷的非遞歸算法

從根結點開始,遍歷左孩子同時壓棧,當遍歷結束,說明當前遍歷的結點沒有左孩子,從棧中取出來調用操作函數,然后訪問該結點的右孩子,繼續以上重復性的操作。

實現代碼函數:
//中序遍歷非遞歸算法
void InOrderTraverse1(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    push(a, Tree);//根結點進棧
    while (top!=-1) {//top!=-1說明棧內不為空,程序繼續運行
        while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL
            push(a, p->lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧
        }
        pop();//跳出循環,棧頂元素肯定為NULL,將NULL彈棧
        if (top!=-1) {
            p=getTop(a);//取棧頂元素
            pop();//棧頂元素彈棧
            displayElem(p);
            push(a, p->rchild);//將p指向的結點的右孩子進棧
        }
    }
}

補:中序遍歷非遞歸算法的另一種實現

中序遍歷過程中,只需將每個結點的左子樹壓棧即可,右子樹不需要壓棧。當結點的左子樹遍歷完成后,只需要以棧頂結點的右孩子為根結點,繼續循環遍歷即可。

實現代碼:
void InOrderTraverse2(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    p=Tree;
    //當p為NULL或者棧為空時,表明樹遍歷完成
    while (p || top!=-1) {
        //如果p不為NULL,將其壓棧並遍歷其左子樹
        if (p) {
            push(a, p);
            p=p->lchild;
        }
        //如果p==NULL,表明左子樹遍歷完成,需要遍歷上一層結點的右子樹
        else{
            p=getTop(a);
            pop();
            displayElem(p);
            p=p->rchild;
        }
    }
}

后序遍歷的非遞歸算法

后序遍歷是在遍歷完當前結點的左右孩子之后,才調用操作函數,所以需要在操作結點進棧時,為每個結點配備一個標志位。當遍歷該結點的左孩子時,設置當前結點的標志位為 0,進棧;當要遍歷該結點的右孩子時,設置當前結點的標志位為 1,進棧。

這樣,當遍歷完成,該結點彈棧時,查看該結點的標志位的值:如果是 0,表示該結點的右孩子還沒有遍歷;反之如果是 1,說明該結點的左右孩子都遍歷完成,可以調用操作函數。

實現代碼函數:
//后序遍歷函數
void PostOrderTraverse(BiTree Tree){
    SNode a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    int tag;
    SNode sdata;
    p=Tree;
    while (p||top!=-1) {
        while (p) {
            //為該結點入棧做准備
            sdata.p=p;
            sdata.tag=0;//由於遍歷是左孩子,設置標志位為0
            postpush(a, sdata);//壓棧
            p=p->lchild;//以該結點為根結點,遍歷左孩子
        }
        sdata=a[top];//取棧頂元素
        pop();//棧頂元素彈棧
        p=sdata.p;
        tag=sdata.tag;
        //如果tag==0,說明該結點還沒有遍歷它的右孩子
        if (tag==0) {
            sdata.p=p;
            sdata.tag=1;
            postpush(a, sdata);//更改該結點的標志位,重新壓棧
            p=p->rchild;//以該結點的右孩子為根結點,重復循環
        }
        //如果取出來的棧頂元素的tag==1,說明此結點左右子樹都遍歷完了,可以調用操作函數了
        else{
            displayElem(p);
            p=NULL;
        }
    }
}

非遞歸算法的完整實現

#include <stdio.h>
#include <string.h>
#define TElemType int
int top=-1;//top變量時刻表示棧頂元素所在位置
//構造結點的結構體
typedef struct BiTNode{
    TElemType data;//數據域
    struct BiTNode *lchild,*rchild;//左右孩子指針
}BiTNode,*BiTree;
//初始化樹的函數
void CreateBiTree(BiTree *T){
    *T=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->data=1;
    (*T)->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->data=2;
    (*T)->lchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->lchild->rchild->data=5;
    (*T)->lchild->rchild->lchild=NULL;
    (*T)->lchild->rchild->rchild=NULL;
    (*T)->rchild->data=3;
    (*T)->rchild->lchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild->lchild->data=6;
    (*T)->rchild->lchild->lchild=NULL;
    (*T)->rchild->lchild->rchild=NULL;
    (*T)->rchild->rchild=(BiTNode*)malloc(sizeof(BiTNode));
    (*T)->rchild->rchild->data=7;
    (*T)->rchild->rchild->lchild=NULL;
    (*T)->rchild->rchild->rchild=NULL;
    (*T)->lchild->lchild->data=4;
    (*T)->lchild->lchild->lchild=NULL;
    (*T)->lchild->lchild->rchild=NULL;
}
//前序和中序遍歷使用的進棧函數
void push(BiTNode** a,BiTNode* elem){
    a[++top]=elem;
}
//彈棧函數
void pop( ){
    if (top==-1) {
        return ;
    }
    top--;
}
//模擬操作結點元素的函數,輸出結點本身的數值
void displayElem(BiTNode* elem){
    printf("%d ",elem->data);
}
//拿到棧頂元素
BiTNode* getTop(BiTNode**a){
    return a[top];
}
//先序遍歷非遞歸算法
void PreOrderTraverse(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    push(a, Tree);//根結點進棧
    while (top!=-1) {
        p=getTop(a);//取棧頂元素
        pop();//彈棧
        while (p) {
            displayElem(p);//調用結點的操作函數
            //如果該結點有右孩子,右孩子進棧
            if (p->rchild) {
                push(a,p->rchild);
            }
            p=p->lchild;//一直指向根結點最后一個左孩子
        }
    }
}
//中序遍歷非遞歸算法
void InOrderTraverse1(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    push(a, Tree);//根結點進棧
    while (top!=-1) {//top!=-1說明棧內不為空,程序繼續運行
        while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL
            push(a, p->lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧
        }
        pop();//跳出循環,棧頂元素肯定為NULL,將NULL彈棧
        if (top!=-1) {
            p=getTop(a);//取棧頂元素
            pop();//棧頂元素彈棧
            displayElem(p);
            push(a, p->rchild);//將p指向的結點的右孩子進棧
        }
    }
}
//中序遍歷實現的另一種方法
void InOrderTraverse2(BiTree Tree){
    BiTNode* a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    p=Tree;
    //當p為NULL或者棧為空時,表明樹遍歷完成
    while (p || top!=-1) {
        //如果p不為NULL,將其壓棧並遍歷其左子樹
        if (p) {
            push(a, p);
            p=p->lchild;
        }
        //如果p==NULL,表明左子樹遍歷完成,需要遍歷上一層結點的右子樹
        else{
            p=getTop(a);
            pop();
            displayElem(p);
            p=p->rchild;
        }
    }
}
//后序遍歷非遞歸算法
typedef struct SNode{
    BiTree p;
    int tag;
}SNode;
//后序遍歷使用的進棧函數
void postpush(SNode *a,SNode sdata){
    a[++top]=sdata;
}
//后序遍歷函數
void PostOrderTraverse(BiTree Tree){
    SNode a[20];//定義一個順序棧
    BiTNode * p;//臨時指針
    int tag;
    SNode sdata;
    p=Tree;
    while (p||top!=-1) {
        while (p) {
            //為該結點入棧做准備
            sdata.p=p;
            sdata.tag=0;//由於遍歷是左孩子,設置標志位為0
            postpush(a, sdata);//壓棧
            p=p->lchild;//以該結點為根結點,遍歷左孩子
        }
        sdata=a[top];//取棧頂元素
        pop();//棧頂元素彈棧
        p=sdata.p;
        tag=sdata.tag;
        //如果tag==0,說明該結點還沒有遍歷它的右孩子
        if (tag==0) {
            sdata.p=p;
            sdata.tag=1;
            postpush(a, sdata);//更改該結點的標志位,重新壓棧
            p=p->rchild;//以該結點的右孩子為根結點,重復循環
        }
        //如果取出來的棧頂元素的tag==1,說明此結點左右子樹都遍歷完了,可以調用操作函數了
        else{
            displayElem(p);
            p=NULL;
        }
    }
}
int main(){
    BiTree Tree;
    CreateBiTree(&Tree);
    printf("前序遍歷: \n");
    PreOrderTraverse(Tree);
    printf("\n中序遍歷算法1: \n");
    InOrderTraverse1(Tree);
    printf("\n中序遍歷算法2: \n");
    InOrderTraverse2(Tree);
    printf("\n后序遍歷: \n");
    PostOrderTraverse(Tree);
}

運行結果

前序遍歷:
1 2 4 5 3 6 7
中序遍歷算法1:
4 2 5 1 6 3 7
中序遍歷算法2:
4 2 5 1 6 3 7
后序遍歷:
4 5 2 6 7 3 1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM