非递归先、中、后序遍历二叉树(C语言)


前言

本程序采用C语言编写,栈和二叉树的基本操作函数基于严蔚敏老师的《数据结构(C语言版)》(清华大学出版社)一书,但参数的传递使用的是C语言中的二级指针,而不是C++中的引用,在CodeBlocks的C语言运行环境下无错误。


一、二叉树非递归遍历算法

1.先序遍历

对于二叉树先序非递归遍历,根结点入栈后立即出栈,然后【<访问根结点并对其右孩子进行判空>。

若不为空,右孩子入栈,然后遍历左子树;

若为空,直接遍历左子树。然后循环<……>(内层while)过程直到左子树为空,退出循环。

然后栈中元素出栈】,循环【……】(外层while)过程,直至栈空。

//函数功能:先序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
//函数参数:二叉树根结点指针T,对数据元素操作的应用函数Visit
//返回值:成功返回OK,失败返回ERROR
Status PreOrderTraverse(BiTree T, Status(*Visit)(TElemType e))
{
    SqStack q,*S;
    S = &q;
    InitStack(S);       //构造一个空栈
    BiTree p;
    Push(S,T);          //根结点入栈
    while(!StackEmpty(S))                   //栈空,退出循环
    {
        Pop(S,&p);      //根结点出栈
        while(p)
        {
            if(!Visit(p->data))     return ERROR;           //访问根结点
            if(p->rchild)               //右孩子判空
            {
                Push(S,p->rchild);              //若右孩子不为空,右孩子入栈
            }
            p = p->lchild;              //遍历左子树
        }
    }
    printf("\nInput Again:");
    return OK;
}

2.中序遍历

对于二叉树中序非递归遍历,【根结点入栈,遍历左子树,循环此过程直至左子树为空,然后<根结点出栈,访问根结点并遍历右子树>】。

若右子树为空,循环<……>(else)过程;

若右子树不为空,循环【……】(while)过程,直至栈空。

//函数功能:中序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
//函数参数:二叉树根结点指针T,对数据元素操作的应用函数Visit
//返回值:成功返回OK,失败返回ERROR
Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType e))
{
    SqStack q,*S;
    S = &q;
    InitStack(S);       //构造一个空栈
    BiTree p;
    p = T;              //p指向根结点
    while(p || !StackEmpty(S))
    {
        if(p)
        {
            Push(S,p);          //根结点入栈
            p = p->lchild;      //遍历左子树
        }
        else
        {
            Pop(S,&p);          //根结点出栈
            if(!Visit(p->data))     return ERROR;               //访问根结点
            p = p->rchild;                              //遍历右子树
        }
    }
    printf("\nInput Again:");
    return OK;
}

3.后序遍历

对于二叉树后序非递归遍历,【根结点入栈,遍历左子树,循环此过程直至左子树为空,然后<根结点出栈,并对其右孩子判空>】。

若右孩子为空,访问根结点,并循环<……>(else+if-else)过程;

若右孩子不为空,用一个辅助指针数组记录该结点的地址,然后该结点再次入栈,遍历其右子树。然后循环【……】(while)过程,直至右孩子为空。

然后<<栈中元素出栈并判断其是否为第二次出栈的右孩子非空的根结点>>。

若是,访问根结点,然后循环<<……>>(else+else if)过程;

若不是,对其右孩子进行判空,判断结果同上。

执行上述过程,直至栈空。

//函数功能:后序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
//函数参数:二叉树根结点指针T,对数据元素操作的应用函数Visit
//返回值:成功返回OK,失败返回ERROR
Status PostOrderTraverse(BiTree T, Status(*Visit)(TElemType e))
{
    SqStack q,*S;
    S = &q;
    InitStack(S);       //构造一个空栈
    TElemType i=-1;                           //指针数组r下标,初始化为-1
    BiTree p,r[STACK_INIT_SIZE];              //辅助指针数组r,用于记录右孩子不为空的结点
    p = T;
    while(p || !StackEmpty(S))
    {
        if(p)
        {
            Push(S,p);                      //根结点入栈
            p = p->lchild;                  //遍历左子树
        }
        else
        {
            Pop(S,&p);                      //根结点出栈
            if(!p->rchild)                  //右孩子判空
            {
                if(!Visit(p->data))     return ERROR;          //若右孩子为空,访问根结点
                p = NULL;                                       //指针p格式化为空,满足下一次循环的出栈条件
            }
            else if(p == r[i])                                 //判断出栈根结点是否为第二次出栈的右孩子非空的根结点
            {
                if(!Visit(p->data))     return ERROR;          //若是,访问根结点
                i--;                                            //指针数组下标减1
                p = NULL;                                       //指针p格式化为空,满足下一次循环的出栈条件
            }
            else
            {
                r[++i] = p;                                     //若右孩子不为空,指针数组r下标加1,记录根结点
                Push(S,p);                                      //根结点再次入栈
                p = p->rchild;                                  //遍历右子树
            }
        }
    }
    printf("\nInput Again:");
    return OK;
}

二、完整程序

#include <stdio.h>
#include <stdlib.h>

#define STACK_INIT_SIZE 100         //存储空间初始分配量
#define STACKINCREMENT 10           //存储空间分配增量
#define OK          1
#define ERROR       0
#define OVERFLOW    -2

typedef int Status;
typedef int TElemType;

/*------二叉树的二叉链表存储结构------*/
typedef struct BiTNode
{
    TElemType       data;                    //结点值
    struct BiTNode  *lchild, *rchild;       //左右孩子指针
}BiTNode, *BiTree;                          //定义结构体别名BiTNode和结构体指针BiTree


/*------栈的顺序存储定义------*/
typedef struct{
    TElemType *base;                //在栈构造之前和销毁之后,base的值为NULL
    TElemType *top;                 //栈顶指针
    int stacksize;                  //当前已分配的存储空间,以元素为单位
}SqStack;

/*函数声明*/
Status InitBiTree(BiTree *T);                                                           //构造空二叉树T
Status CreateBiTree(BiTree *T);                                                         //按先序顺序输入二叉树结点的值(一个字符,#字符表示空树),构建二叉链表表示的二叉树T
Status PreOrderTraverse(BiTree T, Status(*Visit)(TElemType e));                         //先序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType e));                         //中序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
Status PostOrderTraverse(BiTree T, Status(*Visit)(TElemType e));                        //后序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
Status PrintElement(TElemType e);                                                       //Visit函数,输出结点的值
Status InitStack(SqStack *S);                                   //构造一个空栈S
Status StackEmpty(SqStack *S);                                  //判断是否为空栈,是则返回TURE,否则返回FALSE
Status Push(SqStack *S,BiTree e);                               //插入元素e为新的栈顶元素
Status Pop(SqStack *S,BiTree *e);                               //若栈不空,删除栈顶元素并用e返回其值,并返回OK;否则返回ERROR

int main()
{
    BiTree T;
    TElemType x;
	InitBiTree(&T);
	printf("二叉树初始化成功!\n");
	printf("输入二叉树的先序遍历顺序以建立一棵二叉树(以#字符表示空树):\n");
    CreateBiTree(&T);
    printf("请输入对应序号选择遍历二叉树的顺序(1.先序;2.中序;3.后序;0.退出):");
    do{
        scanf("%d",&x);
        switch(x)
        {
            case 1:PreOrderTraverse(T,PrintElement);break;
            case 2:InOrderTraverse(T,PrintElement);break;
            case 3:PostOrderTraverse(T,PrintElement);break;
            case 0:printf("Exit!\n");break;
            default:
                printf("Input Error!\nPlease Input Again:");
        }
    }while(x!=0);
    return 0;
}
//函数功能:构造空二叉树T
//函数参数:二叉树根结点二级指针T
//返回值:成功返回OK,失败返回OVERFLOW
Status InitBiTree(BiTree *T)
{
    (*T)=(BiTree)malloc(sizeof(BiTNode));                   //为根结点分配空间
	if(!(*T)) exit(OVERFLOW);                               //存储分配失败
	(*T)->lchild = NULL;                                    //左孩子置空
	(*T)->rchild = NULL;                                    //右孩子置空
	return OK;
}
//函数功能:按先序顺序输入二叉树结点的值(一个字符,#字符表示空树),构建二叉链表表示的二叉树T
//函数参数:二叉树根结点二级指针T
//返回值:成功返回OK,失败返回OVERFLOW
Status CreateBiTree(BiTree *T)
{
    char ch = getchar();                //从键盘读取字符
    if(ch == '#')                       //#字符表示空树
        *T = NULL;
    else
    {
        *T=(BiTree)malloc(sizeof(BiTNode));
        if(!(*T)) exit(OVERFLOW);
        (*T)->data = ch;                            //生成根结点
        CreateBiTree(&((*T)->lchild));              //构造左子树
        CreateBiTree(&((*T)->rchild));              //构造右子树
    }
    return OK;
}
//函数功能:先序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
//函数参数:二叉树根结点指针T,对数据元素操作的应用函数Visit
//返回值:成功返回OK,失败返回ERROR
Status PreOrderTraverse(BiTree T, Status(*Visit)(TElemType e))
{
    SqStack q,*S;
    S = &q;
    InitStack(S);       //构造一个空栈
    BiTree p;
    Push(S,T);          //根结点入栈
    while(!StackEmpty(S))                   //栈空,退出循环
    {
        Pop(S,&p);      //根结点出栈
        while(p)
        {
            if(!Visit(p->data))     return ERROR;           //访问根结点
            if(p->rchild)               //右孩子判空
            {
                Push(S,p->rchild);              //若右孩子不为空,右孩子入栈
            }
            p = p->lchild;              //遍历左子树
        }
    }
    printf("\nInput Again:");
    return OK;
}
//函数功能:中序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
//函数参数:二叉树根结点指针T,对数据元素操作的应用函数Visit
//返回值:成功返回OK,失败返回ERROR
Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType e))
{
    SqStack q,*S;
    S = &q;
    InitStack(S);       //构造一个空栈
    BiTree p;
    p = T;              //p指向根结点
    while(p || !StackEmpty(S))
    {
        if(p)
        {
            Push(S,p);          //根结点入栈
            p = p->lchild;      //遍历左子树
        }
        else
        {
            Pop(S,&p);          //根结点出栈
            if(!Visit(p->data))     return ERROR;               //访问根结点
            p = p->rchild;                              //遍历右子树
        }
    }
    printf("\nInput Again:");
    return OK;
}
//函数功能:后序遍历二叉树T,对每个结点调用函数Visit一次且仅一次
//函数参数:二叉树根结点指针T,对数据元素操作的应用函数Visit
//返回值:成功返回OK,失败返回ERROR
Status PostOrderTraverse(BiTree T, Status(*Visit)(TElemType e))
{
    SqStack q,*S;
    S = &q;
    InitStack(S);       //构造一个空栈
    TElemType i=-1;                           //指针数组r下标,初始化为-1
    BiTree p,r[STACK_INIT_SIZE];              //辅助指针数组r,用于记录右孩子不为空的结点
    p = T;
    while(p || !StackEmpty(S))
    {
        if(p)
        {
            Push(S,p);                      //根结点入栈
            p = p->lchild;                  //遍历左子树
        }
        else
        {
            Pop(S,&p);                      //根结点出栈
            if(!p->rchild)                  //右孩子判空
            {
                if(!Visit(p->data))     return ERROR;          //若右孩子为空,访问根结点
                p = NULL;                                       //指针p格式化为空,满足下一次循环的出栈条件
            }
            else if(p == r[i])                                 //判断出栈根结点是否为第二次出栈的右孩子非空的根结点
            {
                if(!Visit(p->data))     return ERROR;          //若是,访问根结点
                i--;                                            //指针数组下标减1
                p = NULL;                                       //指针p格式化为空,满足下一次循环的出栈条件
            }
            else
            {
                r[++i] = p;                                     //若右孩子不为空,指针数组r下标加1,记录根结点
                Push(S,p);                                      //根结点再次入栈
                p = p->rchild;                                  //遍历右子树
            }
        }
    }
    printf("\nInput Again:");
    return OK;
}
//函数功能:Visit函数,输出结点的值
//函数参数:结点值e
//返回值:成功返回OK
Status PrintElement(TElemType e)
{
    printf("%c ",e);
    return OK;
}
//函数功能:构造一个空栈
//函数参数:栈S
//返回值:分配成功返回OK,失败返回OVERFLOW
Status InitStack(SqStack *S)
{
    S->base = (TElemType *)malloc(STACK_INIT_SIZE * sizeof(TElemType));
    if(!S->base) exit(OVERFLOW);     //存储分配失败
    /*printf("Arrive!\n");*/
    S->top = S->base;                   //栈顶与栈底相同
    S->stacksize = STACK_INIT_SIZE;             //栈的最大长度等于初始长度
    return OK;
}
//函数功能:判断是否为空栈
//函数参数:栈S
//返回值:是则返回OK,否则返回ERROR
Status StackEmpty(SqStack *S)
{
    if (S->top == S->base)
	{
		return OK;
	}
	return ERROR;
}
//函数功能:插入元素e为新的栈顶元素
//函数参数:栈S,根结点地址e
//返回值:插入成功返回OK,失败返回OVERFLOW
Status Push(SqStack *S,BiTree e)
{
    if(S->top - S->base >= S->stacksize)                //栈满,追加存储空间
    {
        S->base = (TElemType *)realloc(S->base,(S->stacksize + STACKINCREMENT) * sizeof(TElemType));
        if(!S->base) exit(OVERFLOW);                       //存储分配失败
        S->top = S->base + S->stacksize;                // 栈顶指针为栈底指针加上栈之前的最大长度
        S->stacksize += STACKINCREMENT;                 // 栈当前的最大长度等于栈之前的最大长度与增加的长度之和
    }
    *(S->top++) = e;                                      //先赋值(栈中存放的为根结点的地址),后栈顶指针上移
    return OK;
}
//函数功能:若栈不空,删除栈顶元素并用e返回其值,
//函数参数:栈S,根结点二级指针e
//返回值:弹出成功返回OK,否则返回ERROR
Status Pop(SqStack *S,BiTree *e)
{
    if(S->top == S->base) return ERROR;                     //栈空,返回ERROR
    *e = *(--S->top);                                         // 栈顶指针先下移,后赋值(*e为根结点指针存放的地址值,赋值即改变根结点指针的指向)
    return OK;
}


三、运行结果实例

实例1、ABD##E##CF###


实例2、-+a##*b##-c##d##/e##f##

实例3、abdh##int##u###ej##ko##p##cf##gl#q##mr##s##



版权声明:本文为qq_62353814原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。