C语言版:二叉树的前序,中序,后序非递归遍历实现

数据结构:两种遍历方式, 递归和非递归,各有优缺点,都需要掌握。


一、辅助工具

这里需要使用前面文章讲到的栈,利用栈的先进后出的原则,去控制结点的访问。下面就是栈的建立

//栈的建立和基本操作
typedef struct Stack
{
    RTreeNode * sa[MAX];		//存储的元素都是结点
    int top;
}RStack;

//生成一个栈
RStack * init()
{
    RStack *SS;
    SS = (struct Stack*)malloc(sizeof(struct Stack));
    SS->top = 0;
    return SS;
}

//进栈
void push(RStack *p, RTreeNode *x)
{
    if(p->top == MAX)
    {
        printf("栈满");
        exit(0);
    }
    p->sa[p->top] = x;
    ++p->top;
}

//判断是否栈空
bool isEmpty(RStack p)
{
    if(p.top == 0)
    {
        return true;
    }
    return false;
}

//出栈
RTreeNode *pop(RStack *p)
{
    if(isEmpty(*p))
    {
        printf("栈空");
        exit(0);
    }
    return p->sa[--p->top];
}

//清空栈
void clear(RStack *p)
{
    p->top = 0;
}

RTreeNode *GetTop(Stack *S)
{
    if(!isEmpty(*S))
    {
        return S->sa[S->top - 1];       //这里取栈顶元素不需要对栈进行操作
    }
    return NULL;
}

二、递归和非递归的区别

递归:优点简洁明了,代码少。缺点占用计算机资源,执行效率低。 非递归:优点:执行效率高,扩展性强,能够解决一些常见的问题。 缺点:代码多,理解起来比较困难。

三、三种非递归实现过程

1.前序非递归遍历

代码如下(示例):

前序非递归遍历(先遍历根,在遍历左,把右结点的存储在栈中,
最后进行打印 满足了根 左 右)

void front_f_d_preorder(RTreeNode *root)
{
    RStack *S = init();
    push(S, root);
    RTreeNode *p = root;    //设置为根结点
    while(!p || !isEmpty(*S))
    {
        while(p != NULL)
        {
            printf("->%c", p->data);
            if(p->right_child != NULL)
            {
                push(S, p->right_child);    //利用栈的先进后出原则,右子树先进去,反而后出来(保存右结点)
            }
            p = p->left_child;
        }   //遍历左子树的所以左结点  把右结点全部压进栈中
        if(!isEmpty(*S))
        {
            p = pop(S); //右结点出栈
        }
    }
}

2.中序非递归遍历

代码如下(示例):

中序非递归遍历(左 根 右, 先将根 和 左结点压入栈中,
出栈, 然后在判断是否有右子树,有压入栈中,没有进行下一次循环)

void center_f_d_preorder(RTreeNode *root)
{
    RStack *S = init();
    RTreeNode *p = root;       //移动结点
    RTreeNode *q = NULL;       //临时存储出栈结点
    while(p || !isEmpty(*S))
    {
        while(p != NULL)
        {
            push(S,p);
            p = p->left_child;
        }
        while(!isEmpty(*S))
        {
            q = pop(S);
            printf("->%c", q->data);
            if(q->right_child != NULL)
            {
                p = q->right_child; //右子树也需要压入栈中
            }
        }
    }
}

3.后序非递归遍历

代码如下(示例):

后序非递归遍历(左 右 根, 对每个结点都判断是否有右结点,
有压入栈中,将指针赋值给左结点,在压入栈中,每个结点都进行判断)

void after_f_d_preorder(RTreeNode *root)
{
    Stack *S = init();
    RTreeNode *p, *r;
    p = root;       //移动指针
    r = NULL;       //上一次访问的记录(临时指针)
    while(p || !isEmpty(*S))
    {
        if(p != NULL){
            push(S,p);
            p = p->left_child;
        }else{
            p = GetTop(S);  //取栈顶元素
            if(p->right_child != NULL && p->right_child != r)       //判断每一个左结点是否右右子树
            {
                p = p->right_child;     //右结点压入栈中
                push(S,p);
                p = p->left_child;      //左结点压入栈中
            }else{
                p = pop(S);
                printf("->%c", p->data);
                r = p;
                p = NULL;       //结束循环
            }
        }
    }
}

本次非递归遍历的图:在这里插入图片描述

四、代码实现

代码如下(示例):

//
// Created by xoo on 2021/7/7.
//
//采用静态建立二叉树,非递归实现
#include<stdio.h>
#include<stdlib.h>
#include<string>
#define MAX 50

typedef struct TreeNode
{
    struct TreeNode *left_child, *right_child;
    char data;
}RTreeNode;

RTreeNode *init_node(char data, RTreeNode *left_child, RTreeNode *right_child)
{
    RTreeNode *node;
    node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    node->left_child = left_child;
    node->right_child = right_child;
    node->data = data;
    return node;
}

//生成一课二叉树
RTreeNode *create_tree()
{
    RTreeNode *root, *b, *c, *d, *e, *f;
    d = init_node('D', NULL, NULL);
    e = init_node('E', NULL, NULL);
    f = init_node('F', NULL, NULL);
    b = init_node('B', d, NULL);
    c = init_node('C', e, f);
    root = init_node('A', b, c);
    return root;
}

typedef struct Stack
{
    RTreeNode * sa[MAX];
    int top;
}RStack;

//生成一个栈
RStack * init()
{
    RStack *SS;
    SS = (struct Stack*)malloc(sizeof(struct Stack));
    SS->top = 0;
    return SS;
}

//进栈
void push(RStack *p, RTreeNode *x)
{
    if(p->top == MAX)
    {
        printf("栈满");
        exit(0);
    }
    p->sa[p->top] = x;
    ++p->top;
}

//判断是否栈空
bool isEmpty(RStack p)
{
    if(p.top == 0)
    {
        return true;
    }
    return false;
}

//出栈
RTreeNode *pop(RStack *p)
{
    if(isEmpty(*p))
    {
        printf("栈空");
        exit(0);
    }
    return p->sa[--p->top];
}

//清空栈
void clear(RStack *p)
{
    p->top = 0;
}

//取栈顶元素
RTreeNode *GetTop(Stack *S)
{
    if(!isEmpty(*S))
    {
        return S->sa[S->top - 1];       //这里取栈顶元素不需要对栈进行操作
    }
    return NULL;
}
//前序非递归遍历(先遍历根,在遍历左,把右结点的存储在栈中,最后进行打印 满足了根 左 右)
void front_f_d_preorder(RTreeNode *root)
{
    RStack *S = init();
    push(S, root);
    RTreeNode *p = root;    //设置为根结点
    while(!p || !isEmpty(*S))
    {
        while(p != NULL)
        {
            printf("->%c", p->data);
            if(p->right_child != NULL)
            {
                push(S, p->right_child);    //利用栈的先进后出原则,右子树先进去,反而后出来(保存右结点)
            }
            p = p->left_child;
        }   //遍历左子树的所以左结点  把右结点全部压进栈中
        if(!isEmpty(*S))
        {
            p = pop(S); //右结点出栈
        }
    }
}

//中序非递归遍历(左 根 右, 先将根 和 左结点压入栈中,出栈, 然后在判断是否有右子树,有压入栈中,没有进行下一次循环)
void center_f_d_preorder(RTreeNode *root)
{
    RStack *S = init();
    RTreeNode *p = root;       //移动结点
    RTreeNode *q = NULL;       //临时存储出栈结点
    while(p || !isEmpty(*S))
    {
        while(p != NULL)
        {
            push(S,p);
            p = p->left_child;
        }
        while(!isEmpty(*S))
        {
            q = pop(S);
            printf("->%c", q->data);
            if(q->right_child != NULL)
            {
                p = q->right_child; //右子树也需要压入栈中
            }
        }
    }
}

//后序非递归遍历(左 右 根, 对每个结点都判断是否有右结点,有压入栈中,将指针赋值给左结点,在压入栈中,每个结点都进行判断)
void after_f_d_preorder(RTreeNode *root)
{
    Stack *S = init();
    RTreeNode *p, *r;
    p = root;       //移动指针
    r = NULL;       //上一次访问的记录(临时指针)
    while(p || !isEmpty(*S))
    {
        if(p != NULL){
            push(S,p);
            p = p->left_child;
        }else{
            p = GetTop(S);  //取栈顶元素
            if(p->right_child != NULL && p->right_child != r)       //判断每一个左结点是否右右子树
            {
                p = p->right_child;     //右结点压入栈中
                push(S,p);
                p = p->left_child;      //左结点压入栈中
            }else{
                p = pop(S);
                printf("->%c", p->data);
                r = p;
                p = NULL;       //结束循环
            }
        }
    }
}

//先序遍历
void front_preorder(RTreeNode *p)
{
    if(p != NULL){
        printf("->%c", p->data);
        front_preorder(p->left_child);
        front_preorder(p->right_child);
    }
}

//中序遍历
void center_preorder(RTreeNode *p)
{
    if(p != NULL){
        center_preorder(p->left_child);
        printf("->%c", p->data);
        center_preorder(p->right_child);
    }
}

//后序遍历
void after_preorder(RTreeNode *p)
{
    if(p != NULL){
        after_preorder(p->left_child);
        after_preorder(p->right_child);
        printf("->%c", p->data);
    }
}

int main() {
    RTreeNode *tree;
    tree = create_tree();
    printf("非递归的前序遍历:");
    front_f_d_preorder(tree);
    printf("\n");
    printf("递归的前序遍历:");
    front_preorder(tree);
    printf("\n");
    printf("非递归的中序遍历:");
    center_f_d_preorder(tree);
    printf("\n");
    printf("递归的中序遍历:");
    center_preorder(tree);
    printf("\n");
    printf("非递归的后序遍历:");
    after_f_d_preorder(tree);
    printf("\n");
    printf("递归的后序遍历:");
    after_preorder(tree);
    printf("\n");
    system("pause");
    return 0;
}

结果展示:在这里插入图片描述


总结

非递归的运用有很多,所以一定好好掌握,前序和中序非递归是容易理解的,后序非递归比较难理解,所以一定需要注重画图,具体的思路可以去某站上搜索。

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值