二叉树的创建以及遍历C语言

二叉树的C语言创建以及遍历

今天在看二叉树相关的知识,于是乎便想实现一下,接下来给上我的代码
首先是bitree.h

/*
 * Description: releated data struct 
 * author: badapple
 * file: bitree.h
 */
 #include <stdio.h>
 #include <stdlib.h>

typedef struct bi_node //二叉树节点定义以及别名
{
    char data;
    struct bi_node *lchild;
    struct bi_node *rchild;
}bitree, binode;

sttic void bitree_create(bitree ** t) //用前序递归创建一棵二叉树
{
    char data;
    binode *node;
    node = *t;
    fflush(stdin);
    scanf("%c", &data);
    
    if(data == "#")
    	node = NULL;
    else
    {
        node = (binode *)malloc(binode);
        node->data = data;
        bitree_create(&(node->lchild));
        bitree_create(&(node->rchild));
    }
    //以上是网上的版本,这样创建的二叉树在这个子函数里是存在,但是当你主函数*bitree永远是空,不知道其他博主怎么能创建一棵树的
    *t = node; //添加的代码
}

为了让代码可读性强点,又创建了bitree_recursion.h,里面放递归算法实现的遍历函数

/*
 * Description: use recursion to traverse a binary tree 
 * author: badapple
 * file: bitree_recursion.h
 */
#include "bitree.h"

static void preorder_traverse(bitree *t) //DLR
{
    if(t)
    {
        printf("%-2c ", t->data);
        preorder_traverse(t->lchild);
        preorder_traverse(t->rchild);
    }
}

static void inorder_traverse(bitree *t) //LDR
{
    if(t)
    {
        preorder_traverse(t->lchild);
        printf("%-2c ", t->data);
        preorder_traverse(t->rchild);
    }
}

static void postorder_traverse(bitree *t) //LRD
{
    if(t)
    {
        preorder_traverse(t->lchild);
        preorder_traverse(t->rchild);
        printf("%-2c ", t->data);
    }
}

测试文件test.c

#include "bitree_recursion.h"

int main()
{
    bitree *t;
    t = NULL;
    create_bitree(&t);
    preorder_traverse(t);
    putchar(10);
    inorder_traverse(t);
    putchar(10);
    postorder_traverse(t);
    putchar(10);
    
    return 0;
}

测试的二叉树如图,结果如图所示
二叉树结构
如果将bitree.h中注释添加的代码,即

//*t = node

那么结果如下
测试结果OK上述都是其中的插曲~,主要问题是遍历算法的非递归实现

编辑bitree.h,加入以下代码

typedef struct stack_node //定义栈结构体
{
    bitree *data; //栈存储的元素
    struct stack_node *next;
}stack;

static stack *stack_init() //栈初始化函数
{
    stack *s;
    s = (stack *)malloc(sizeof(stack));
    s->data = NULL;
    s->next = NULL;
    return s;
}

static void stack_push(stack *s, binode *node) //入栈操作
{
    stack *pos, *tmp;
    tmp = (stack *)malloc(sizeof(stack)); //新建栈节点
    tmp->data = node; //将二叉树节点存入到栈节点内
    tmp->next = NULL;
    if(!s->next) //如果栈空
        s->next = tmp;
    else
    {
        pos = s->next; //使用pos暂存现在的栈顶元素
        s->next = tmp; //将tmp作为新的栈顶元素
        tmp->next = pos; //将原先的栈顶元素链接到现在的栈顶元素后面
    }
}

static binode *stack_get(stack *s) //获取栈顶元素的值
{
    if(!s->next)
        return NULL;
    return s->next->data;
}

static void stck_pop(stack *s) //出栈操作
{
    stack *s;
    if(!s->next) //如果栈空
        return;
    pos = s->next; //暂存栈顶元素
    s->next = pos->next; //将栈顶元素指向原栈顶元素的下一位
    free(pos); //释放原先的栈顶元素
    return;
}

OK,关于栈的操作已写好,接下来就用非递归方式实现遍历

新建bitree_no_recursion.h

#include "bitree_no_recursion.h"

static void preorder_traverse_no_recursion(bitree *t) //非递归先序遍历
{
    stack *s; //声明栈
    bitree *pos; //定义遍历指针
    s = stack_init(); //初始化栈,返回栈顶指针
    pos = t; //将二叉树root赋值给遍历指针
    while(pos || s->next) //当树非空或者栈非空
    {
        if(pos) //如果树非空
        {
            stack_push(s, pos); //将这个树节点压栈
            printf("%-2c ", pos->data);
            pos = pos->lchild; //遍历指针指向左边的叶子
        }
        else
        {
            pos = stack_get(s); //获取栈顶元素
            stack_pop(s); //出栈操作
            pos = pos->rchild; //指向右边叶子节点
        }   
    }
}

static void inorder_traverse_no)recursion(bitree *t) //非递归中序遍历
{
    stack *s; //声明栈
    bitree *pos; //定义遍历指针
    s = stack_init(); //初始化栈,返回栈顶指针
    pos = t; //将二叉树root赋值给遍历指针
	while(pos || s->next) //当树非空或者栈非空
    {
        if(pos)
        {
            stack_push(s, pos); //将这个树节点压栈
            pos = pos->lchild; //遍历指针指向左节点
        }
        else
        {
            pos = stack_get(s); //获取栈顶元素
            printf("%-2c ", pos->data); //输出
            pos = pos->rchild; //遍历指针指向右节点
        }
    }
}

static void postorder_traverse(bitree *t) //非递归后序遍历
{
    stack *s;
    bitree *pos, *last_visit;
    s = stack_init();
    pos = t, last_visit = NULL;
    while(pos) //将pos指针指向树的最左边
    {
        stack_push(s, pos);
        pos = pos->lchild;
    }
    while(s->next) //当栈非空
    {
        pos = stack_get(s);
        stack_pop(s);
        if(!(pos->rchild) || pos->rchild == last_visit) //当节点的右节点为空或已被访问
        {
            printf("%-2c ", pos->data);
            last_visit = pos;
        }
        else
        {
            stack_push(s, pos);
            pos = pos->rchild;
            while(pos)
            {
                stack_push(s, pos);
                pos = pos->lchild);
            }
        }
    }
}

修改test.c,测试非递归遍历算法

#include "bitree_no_recursion.h"
int main()
{
    bitree *t;
    t = NULL;
    create_bitree(&t);
    preorder_traverse_no_recursion(t);
    putchar(10);
    inorder_traverse_no_recursion(t);
    putchar(10);
    postorder_traverse_no_recursion(t);
    putchar(10);
    return 0;
}

结果如图
在这里插入图片描述

总结

这三种遍历算法的非递归实现,其实都是参考的递归的算法。只是递归算法本质上也是栈,只是栈的元素是函数,我们非递归实现就是将栈的元素从函数变成我们的数据结构,从而减少开销

后序遍历相较于其他两个算法稍微难点,举个例子
在这里插入图片描述
第一步,我们将遍历指针指向二叉树的最左边,即图中的节点C

第二步,我们想想,后序遍历的顺序是左右根。那么想要遍历某个节点前提便是左右节点都已经遍历过,由于开始的时候我们就已经找到树的最左边节点CC的左子树肯定为空。当C的右子树为空或者右子树已经被遍历过,那么我们就可以遍历右子树,即代码

if(!(pos->rchild) || pos->rchild == NULL)

第三步,②情况能理解,可能③情况不理解。当遍历到③这种情况,很像②,但是与②不同的是,E节点左节点不为空,那么自然不能直接输出E节点。这种情况,我们就将F视作根节点。遍历F的前提是F左右子树皆遍历过,这不就又回到①情况。

PS

操作系统OS:Ubuntu 18.04 LTS

gcc version:7.3.0

表达能力不是很好,见谅了!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值