二叉树的非递归遍历(前中后)c++

前序非递归

1.结点不空则先访问后入栈,而后沿着此结点的左孩子,
依次访问后并入栈,直到左孩子为空//说明左子树的根已经访问结束
2.栈顶元素出栈并访问右孩子,
若右孩子为空,则继续执行2 ;
若右孩子不空,则继续执行1 ;

void PreOrderNonR(BiTree T){
    LinkStack S;
    InitStack(S);
    BiTree p=T;//初始化栈,p是遍历指针
    while(p || !StackEmpty(S)){//栈不空或p不为空时循环
        if(p){                    //一路向左
            visit(p);Push(S,p);//访问当前结点,并入栈
            p=p->lchild;//左孩子不空,一直向左走
        }
        else{
            //printf("# ");//p为空
            Pop(S,p);//栈顶元素出栈,并转向出栈结点的右孩子
            p=p->rchild;//向右子树走,p赋值为当前结点的右孩子
                        //返回while循环继续进行if-else语句
        }
    }
}

中序非递归

1.沿着根的孩子,依次入栈,直到左孩子为空,说明已经找到可以输出端 结点
2.栈顶元素出栈并访问:
若其右孩子为空,继续执行;
若其右孩子不空,将右子树转执行1.

void InOrderNonR(BiTree T){
    LinkStack S;
    InitStack(S);//初始化栈
    BiTree p=T;//p是遍历指针
    while(p || !StackEmpty(S)){//栈不空或p不空时循环
        if(p){
            Push(S,p);//当前结点入栈
            p=p->lchild;//左孩子不空,一直向左走
        }
        else{
            //printf("# ");//p为空
            Pop(S,p);//出栈并访问,转向出栈结点的右子树
            visit(p);//栈顶元素出栈,访问出栈结点
            p=p->rchild;//向右子树走,p赋值为当前结点的右孩子
        }
    }
    /*//不用销毁,因为Push过程动态分配,Pop过程动态销毁
    if(StackEmpty(S)){
        printf("\n\n栈空,那我要销毁了栈\n");
        DestroyStack(S);
    }*/
}

后序非递归

1.从根节点开始,将其入栈,然后沿着其左子树一直往下搜索,直到搜索到没有左孩子的结点。
但是此时不能出栈并访问,因为如果其有右子树,还需按相同的规则对其右子树进行处理
2.直至上述操作进行不下去,
若栈顶元素想要出栈被访问,要么右子树为空,要么右子树刚被访问过(此时左子树早已访问过了)

后序遍历:后序遍历较前两种遍历方法比较难实现,原因在于需要遍历完左子树,遍历完右子树,最后才去访问根节点。这样栈顶结点可能会从他的左子树返回,也有可能从他的右子树返回,需要区分这种情况,如果是第一次从左子树返回,那么还需要去遍历其右子树,如果是从右子树返回,那么直接返回该结点就可以了。这里使用辅助指针来区分来源。

要注意:在 visit( p ) 后
要将辅助指针r指向刚刚被访问过的结点,并将 p 赋值为NULL,因为既然p能被访问,说明p的左右结点均已被访问过了,则对于p来说,p是其父节点的右孩子,所以p访问过后,要从栈中取出下一个或许被访问的结点,故应将p = NULL

非递归后序遍历二叉树时,访问一个结点p时,栈中结点恰好是p结点的所有祖先,从栈底到栈顶再加上结点p,刚好构成从根节点到p结点的一条路径。在很多算法设计中都可以利用这一思路来求解
如:求根到某结点的路径,求两个结点的最近公共祖先等。

中序遍历参考出处

void PostOrderNonR(BiTree T){
    LinkStack S;
    InitStack(S);
    BiTree p=T;//p是遍历指针
    BiTree r=NULL;//辅助指针r,指向刚刚被访问过的结点
    while(p || !StackEmpty(S)){
        if(p){
            Push(S,p);//左节点入栈
            p=p->lchild;//沿着其左子树一直往下搜索,直到没有左孩子的结点
        }
        else{
            GetTop(S,p);//@@@取栈顶,!!!注意,不是出栈!
            if(p->rchild && p->rchild != r ){//@@@
                //右子树还没有访问 且 右子树非空
                p=p->rchild;//进入右子树
            }
            else{//右子树已经访问过 或 右子树为空,则出栈并访问结点
                Pop(S,p);
                visit(p);
                r=p;//指向访问过的结点
                p=NULL;//@@@ 使p为空,继续访问栈顶@@@
            }

        }
    }
}

完整代码测试

完整代码如下所示:

#include<stdio.h>
#include<stdlib.h>
#define Elemtype BiTree
#define ElemType char

typedef struct BiTNode{//栈的链式存储
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;


typedef struct LSNode{//链结点
    Elemtype data;//数据域
    struct LSNode *next;//指针域
}LSNode,*LinkStack;



//这里规定链栈没有头结点
void InitStack(LinkStack &s){
    s=NULL;
}
bool StackEmpty(LinkStack s){
    return s==NULL;
}

bool Push(LinkStack &s,Elemtype x){
    if(s==NULL){//空栈    则此时入栈即为第一个元素
        s=(LinkStack)malloc(sizeof(LSNode));
        s->data=x;
        s->next=NULL;
        return true;
    }

    LSNode *p=(LSNode *)malloc(sizeof(LSNode));
    if(p==NULL){
        printf("p==NULL\n");
        return false;
    }
    //偷天换日  用后插的方法来实现前插,即实现入栈
    p->data=s->data;
    p->next=s->next;
    s->next=p;
    s->data=x;
    return true;
}

bool Pop(LinkStack &s,Elemtype &x){
    if(s==NULL){
        printf("s==NULL\n");
        return false;
    }
    LSNode *p=s;
    s=s->next;
    x=p->data;
    free(p);
    p=NULL;
    return true;
}

bool GetTop(LinkStack s, Elemtype &x){
    if(s==NULL){
        printf("空栈\n");
        return false;
    }
    x=s->data;
    return true;
}

bool DestroyStack(LinkStack &s){
    if(s==NULL){
        printf("栈不存在,无法销毁\n");
        return false;
    }
    LSNode*p=s;
    while(p!=NULL){
        s=s->next;
        free(p);
        p=s;
    }
    p=NULL;
    return true;
}

bool PrintStack(LinkStack s){
    if(s==NULL){
        printf("空栈,无法print\n");
        return false;
    }
    printf("\nStack is: ");
    LSNode *p=s;
    while(p){
        printf("%d ",p->data);
        p=p->next;//@@@
    }
    printf("\n");
    return true;
}

void visit(BiTree T){
        printf("%c ",T->data);
}

//前序(先根遍历)非递归 Non-Recurrence
/*
1.结点不空则先访问后入栈,而后沿着此结点的左孩子,
  依次访问后并入栈,直到左孩子为空//说明左子树的根已经访问结束
2.栈顶元素出栈并访问右孩子,
    若右孩子为空,则继续执行2 ;
    若右孩子不空,则继续执行1 ;
*/
void PreOrderNonR(BiTree T){
    LinkStack S;
    InitStack(S);
    BiTree p=T;//初始化栈,p是遍历指针
    while(p || !StackEmpty(S)){//栈不空或p不为空时循环
        if(p){                    //一路向左
            visit(p);Push(S,p);//访问当前结点,并入栈
            p=p->lchild;//左孩子不空,一直向左走
        }
        else{
            //printf("# ");//p为空
            Pop(S,p);//栈顶元素出栈,并转向出栈结点的右孩子
            p=p->rchild;//向右子树走,p赋值为当前结点的右孩子
                        //返回while循环继续进行if-else语句
        }
    }
}

//中序非递归Non-Recurrence
/*
1.沿着根的孩子,依次入栈,直到左孩子为空,说明已经找到可以输出端 结点
2.栈顶元素出栈并访问:
    若其右孩子为空,继续执行;
    若其右孩子不空,将右子树转执行1.
*/
void InOrderNonR(BiTree T){
    LinkStack S;
    InitStack(S);//初始化栈
    BiTree p=T;//p是遍历指针
    while(p || !StackEmpty(S)){//栈不空或p不空时循环
        if(p){
            Push(S,p);//当前结点入栈
            p=p->lchild;//左孩子不空,一直向左走
        }
        else{
            //printf("# ");//p为空
            Pop(S,p);//出栈并访问,转向出栈结点的右子树
            visit(p);//栈顶元素出栈,访问出栈结点
            p=p->rchild;//向右子树走,p赋值为当前结点的右孩子
        }
    }
    /*//不用销毁,因为Push过程动态分配,Pop过程动态销毁
    if(StackEmpty(S)){
        printf("\n\n栈空,那我要销毁了栈\n");
        DestroyStack(S);
    }*/
}


//后序非递归 Non-Recurrence
/*左右根    要弄两个指针
1.从根节点开始,将其入栈,然后沿着其左子树一直往下搜索,直到搜索到没有左孩子的结点。
  但是此时不能出栈并访问,因为如果其有右子树,还需按相同的规则对其右子树进行处理
2.直至上述操作进行不下去,
  若栈顶元素想要出栈被访问,要么右子树为空,要么右子树刚被访问过(此时左子树早已访问过了)
*/
void PostOrderNonR(BiTree T){
    LinkStack S;
    InitStack(S);
    BiTree p=T;//p是遍历指针
    BiTree r=NULL;//辅助指针r,指向刚刚被访问过的结点
    while(p || !StackEmpty(S)){
        if(p){
            Push(S,p);//左节点入栈
            p=p->lchild;//沿着其左子树一直往下搜索,直到没有左孩子的结点
        }
        else{
            GetTop(S,p);//@@@取栈顶,!!!注意,不是出栈!
            if(p->rchild && p->rchild != r ){//@@@
                //右子树还没有访问 且 右子树非空
                p=p->rchild;//进入右子树
            }
            else{//右子树已经访问过 或 右子树为空,则出栈并访问结点
                Pop(S,p);
                visit(p);
                r=p;//指向访问过的结点
                p=NULL;//@@@ 使p为空,继续访问栈顶@@@
            }

        }
    }


}



//按前序输入二叉树中节点的值(一个字符)
// ' '表示空树, 构造二叉链表表示二叉树T
bool CreateBiTree(BiTree &T){
    ElemType ch;

    scanf("%c",&ch);//@@@
    if(ch == '#'){
        //printf("您要创建一棵空树吗?\n");
        T=NULL;//
        return false;
    }
    else{
        T=(BiTree)malloc(sizeof(BiTNode));
        if(!T){
            printf("malloc failure\n");
            return false;
        }
        T->data=ch;//生成根节点
        CreateBiTree(T->lchild);//构造左子树
        CreateBiTree(T->rchild);//构造右子树
        return true;
    }
}


bool DestroyBiTree(BiTree T){//@@@
    if(T == NULL){
        //printf("空结点#\n");
        return false;
    }
    DestroyBiTree(T->lchild);
    DestroyBiTree(T->rchild);
    printf("销毁%c\n",T->data);
    free(T);//@@@
    T=NULL;//防止产生野指针
    return true;
}

int main(){
    BiTree T=NULL;//@@@
    printf("按前序输入二叉树中节点的值(输入#表示空节点)\n");
    CreateBiTree(T);
    printf("NonRecurrence前序遍历结果为:\n");
    PreOrderNonR(T);
    printf("\n");

    printf("NonRecurrence非递归中序遍历结果为:\n");
    InOrderNonR(T);
    printf("\n");

    printf("NonRecurrence非递归后序遍历结果为:\n");
    PostOrderNonR(T);

    printf("\n\n开始destroy二叉树(按后序遍历来销毁):\n\n");
    DestroyBiTree(T);
    return 0;
}

在输入窗口输入: AB#D##C##
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值