数据结构-二叉树

本文介绍了二叉树的前序、中序、后序遍历的递归与非递归实现,包括栈和队列在遍历中的作用。同时,展示了层次遍历和Morris遍历方法。代码示例涵盖了二叉树节点定义、辅助队列结构以及相关操作函数。
摘要由CSDN通过智能技术生成

function.h

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

typedef char BiElemType;

//二叉树结点的结构体定义
typedef struct BiTNode {
    BiElemType c;//c就是书上的data
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode, * BiTree;//12个字节的空间

//辅助队列结点的结构体定义
typedef struct tag {
    BiTree p;//树的某一个结点的地址值
    struct tag* pnext;
}tag_t, * ptag_t;//链表,放的是指针的类型,原因:存放的是树的某一个结点的地址值
//为什么要用辅助队列:当没次建树加结点的时候,

//栈的相关数据结构
#define MaxSize 50
typedef BiTree ElemType;
typedef struct {
    ElemType data[MaxSize];
    int top;
}SqStack;
//声明放在头文件中的目的:声明一次即可,在其他文件中要引用函数时,#include 头文件即可,不用多次声明
void InitStack(SqStack& S);
bool StackEmpty(SqStack& S);
bool Push(SqStack& S, ElemType x);
bool Pop(SqStack& S, ElemType& x);
bool GetTop(SqStack& S, ElemType& x);

//队列的相关数据结构(链队)
typedef struct LinkNode {
    ElemType data;
    struct LinkNode* next;
}LinkNode;

//单独定义队头、队尾的指针
typedef struct {
    LinkNode* front, * rear;
}LinkQueue;

void InitQueue(LinkQueue& Q);
bool IsEmpty(LinkQueue Q);
void EnQueue(LinkQueue& Q, ElemType x);
bool DeQueue(LinkQueue& Q, ElemType& x);

main.cpp

#include "function.h"
#define _CRT_SECURE_NO_WARNINGS



//递归实现
//abdhiejcfg
void preOrder(BiTree p)//前序遍历,先序遍历,(深度优先遍历)先打印当前节点,再打印左孩子,然后打印右孩子。考纲要求
{//把一颗树传进来。
    if (p != NULL)
    {
        putchar(p->c);//等价于visit函数
        preOrder(p->lchild);
        preOrder(p->rchild);
    }
}

//中序遍历  hdibjeafcg先打印左孩子,再打印父亲,再打印右孩子。考纲要求
void InOrder(BiTree p)
{
    if (p != NULL)
    {
        InOrder(p->lchild);
        putchar(p->c);
        InOrder(p->rchild);
    }
}
//hidjebfgca

void PostOrder(BiTree p)//后序遍历,先打印左孩子,再打印右孩子,最后打印父亲。考纲要求
{
    if (p != NULL)
    {
        PostOrder(p->lchild);
        PostOrder(p->rchild);
        putchar(p->c);
    }
}

//中序遍历非递归,非递归执行效率更高,考的概率很低。考纲无要求
void InOrder2(BiTree T)
{
    SqStack S;
    InitStack(S); BiTree p = T;//p是遍历指针
    while (p || !StackEmpty(S))//p非空或者栈非空
    {
        if (p)
        {
            Push(S, p);//一个结点不为空,不断将左孩子压栈的过程,找最左孩子
            p = p->lchild;//一路向左,找到最左结点
        }
        else {//找到最左结点后,接着要依次访问其父结点及其右兄弟(中序遍历),弹出栈中元素并打印,获取打印元素的右结点
            Pop(S, p); putchar(p->c);//putchar(p->c)相当于visit(p)函数
            p = p->rchild;
        }
    }
}

//层次遍历,广度优先遍历,一层一层的打。考纲无要求
void LevelOrder(BiTree T)
{
    LinkQueue Q;
    InitQueue(Q);

    BiTree p = T;
    EnQueue(Q, T);//树根入队
    while (!IsEmpty(Q))
    {
        DeQueue(Q, p);//出队列的头元素
        putchar(p->c);//打印有头元素
        if (p->lchild != NULL)
            EnQueue(Q, p->lchild);//入队左孩子
        if (p->rchild != NULL)
            EnQueue(Q, p->rchild);//入队右孩子
    }
}


//不用栈实现二叉树的中序遍历 hdibjeafcg。考纲要求
void bst_morris_inorder( BiTree root)//root相当于tree
{

    BiTree tmp,p = root;
    while (p) {

        if (p->lchild == NULL) {

            printf("%c", p->c);

            p = p->rchild;

        }

        else {

            tmp = p->lchild;//tree左子树赋给tmp

            while (tmp->rchild != NULL && tmp->rchild != p)

                tmp = tmp->rchild;

            if (tmp->rchild == NULL) {

                tmp->rchild = p;

                p = p->lchild;

            }

            else {

                printf("%c", p->c);

                tmp->rchild = NULL;

                p = p->rchild;

            }

        }

    }

}
//层序建树——借助逻辑上的辅助队列
int main() {
    BiTree tree = NULL;//树的根结点
    BiTree tpnew;//指向树的新结点的指针
    ptag_t phead = NULL, ptail = NULL, listpnew = NULL, pcur = NULL;
    //phead是队列头,ptail是队列尾部,通过pcur访问到p,再由p访问到ptail和phead;pcur->p->ptail/phead
    char c;
    //辅助队列中的队头,队尾结点,指向新结点的listpnew,指向当前插入位置的父结点的pcur
    //abcdefg
    while (scanf_s("%c", &c) != EOF) 
    {
        if ('\n' == c) 
        {
            break;//结束循环
        }//当一次性输入多个元素后,因为回车,这跳出循环,不需要再进行ctrl+z结束循环
        //分配空间
        tpnew = (BiTree)calloc(1, sizeof(BiTNode));//给树结点分配空间,并且每个的空间都赋值为NULL
        tpnew->c = c;//输入的元素放入树结点中c相当于data
        listpnew = (ptag_t)calloc(1, sizeof(tag_t));//给队列结点分配空间
        listpnew->p = tpnew;//树的结点地址放入队列结点中
        //tpnew代表的是树的某一个结点,Listpnew代表的是一个链表,存放tree的某一个结点,和*(地址)
        if (NULL == tree) {
            tree = tpnew;
            phead = listpnew;//队列头
            ptail = listpnew;//队列尾
            pcur = listpnew;//pcur始终指向插入位置的父结点
            continue;//继续下一轮循环,跳出这个if语句,回到while读取scanf
        }
        else {
            ptail->pnext = listpnew;
            ptail = listpnew;//队列中使用尾插法,ptail指向队列尾部
        }
        if (NULL == pcur->p->lchild) {//通过队列找到树中结点,把结点放入树
            pcur->p->lchild = tpnew;//把新结点插入到要插入结点的左边
        }//pcur始终指向要插入的结点的位置
        else if (NULL == pcur->p->rchild) {
            pcur->p->rchild = tpnew;//把新节点放入到要插入结点的右边
            pcur = pcur->pnext;//以a为根节点的左右子树已经插好,逻辑上需要将a出队,插入位置的父节点由a变为了b,所以pcur要后移
        }//左右都放了结点后,pcur指向队列的下一个,并没有出队操作

    }
    printf("\n");

    printf("-----------前序遍历---------\n");
    preOrder(tree);
    printf("\n");

    printf("-----------中序遍历---------\n");
    InOrder(tree);
    printf("\n");

    printf("-----------后序遍历---------\n");
    PostOrder(tree);
    printf("\n");

    printf("-----------中序遍历非递归使用栈---------\n");
    InOrder2(tree);
    printf("\n");

    printf("-----------层次遍历---------\n");
    LevelOrder(tree);
    printf("\n");

    printf("-----------中序遍历非递归未使用栈---------\n");
    bst_morris_inorder(tree);
}

queue.cpp

#include "function.h"
//带头结点的链队——头结点的下一个节点才有数据
//初始化
void InitQueue(LinkQueue& Q)
{
    Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
    Q.front->next = NULL;
}

//判空
bool IsEmpty(LinkQueue Q)
{
    if (Q.front == Q.rear)
        return true;
    else
        return false;
}

//入队、出队操作当作链表处理
//入队——尾插法
void EnQueue(LinkQueue& Q, ElemType x)
{
    LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
    s->data = x; s->next = NULL;
    Q.rear->next = s;
    Q.rear = s;
}

//出队——头部删除法
bool DeQueue(LinkQueue& Q, ElemType& x)
{
    if (Q.front == Q.rear) return false;//队空不出
    LinkNode* p = Q.front->next;//头结点什么都没存,所以头结点的下一个节点才有数据(带头结点的链队)
    x = p->data;
    Q.front->next = p->next;
    if (Q.rear == p)
        Q.rear = Q.front;
    free(p);
    return true;
}

stack.cpp

#include "function.h"


void InitStack(SqStack& S)
{
    S.top = -1;
}

bool StackEmpty(SqStack& S)
{
    if (S.top == -1)
        return true;
    else
        return false;
}
//入栈
bool Push(SqStack& S, ElemType x)
{
    if (S.top == MaxSize - 1)
    {
        return false;
    }
    S.data[++S.top] = x;
    return true;
}
//出栈
bool Pop(SqStack& S, ElemType& x)
{
    if (-1 == S.top)
        return false;
    x = S.data[S.top--];
    return true;
}
//读取栈顶元素
bool GetTop(SqStack& S, ElemType& x)
{
    if (-1 == S.top)
        return false;
    x = S.data[S.top];
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值