二叉树操作大全(C实现)

以下代码包括:辅助栈(链栈)、辅助队列(顺序存储循环队列)、二叉树的两种先序建立、先序中序后序遍历(递归)、中序遍历非递归方法、层次遍历。注释充分,逻辑清晰,代码可读性强。

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>

typedef char BiElemType;
typedef struct BTNode{
    BiElemType data;  //数据域
    struct BTNode * pLchild;
    struct BTNode * pRchild;
}BTNode, * BiTree;

//======================辅助栈的实现========================
typedef struct Node{
    BiTree data;
    struct Node * next;
}NODE, * PNODE;

typedef struct Stack{
    PNODE pTop;   //栈顶
    PNODE pBottom;//栈底
}STACK, * PSTACK;

void init_S(PSTACK pS){//链栈的初始化
    PNODE pHead = (PNODE)malloc(sizeof(NODE));
    if (pHead == NULL)
    {
        printf("内存分配失败!\n");
    }
    pS->pBottom = pHead;  //初始栈顶栈底都指向头节点
    pS->pTop = pHead;
    pS->pTop->next = NULL;

}

void push(PSTACK pS, BiTree val){ //原则:先入栈后变动栈顶指针
    PNODE pnew = (PNODE)malloc(sizeof(NODE));
    pnew->data = val;
    pnew->next = pS->pTop; //将新节点接在原栈顶上方
    pS->pTop = pnew;  //栈顶指针变动至最新位置
    return;
}

void traverse(PSTACK pS){
    PNODE p = pS->pTop;  //遍历指针获取到栈顶
    while(p != pS->pBottom){
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
    return;
}

//出栈一次,并将元素输出在val中
bool pop(PSTACK pS, BiTree * p){  //这里用到了二级指针
    if(pS->pBottom == pS->pTop){  //栈空报错
        return false;
    }
    *p = pS->pTop->data;  //保存删去的值
    PNODE q = pS->pTop;
    pS->pTop = q->next;  //移动栈顶指针
    free(q);
    return true;
}

bool is_empty(PSTACK S){
    if(S->pBottom == S->pTop){  //栈空
        return true;
    }else{
        return false;
    }
}

//======================辅助队列的实现========================
#define MAX_SIZE 6   //宏定义  数组长度为6,最多能保存5个元素

typedef struct Queue
{
    BiTree * pBase;  //数组的首地址
    int front;
    int rear;
}QUEUE;

void init_Q(QUEUE * pQ){ //初始化一个长度为6的队列
    pQ->pBase = (int *)malloc(sizeof(int) * MAX_SIZE);
    pQ->front = 0;
    pQ->rear = 0;
}

//判断队列是否为空(引入一个空单元作为牺牲)
bool is_full(QUEUE * pQ){
    if((pQ->rear + 1) % MAX_SIZE == pQ->front){
        printf("该循环队列已满!\n");
        return true;
    }
    return false;
}

bool q_empty(QUEUE * pQ){
    if(pQ->rear == pQ->front){
        return true;
    }else{
        return false;
    }
}

bool en_queue(QUEUE * pQ, PNODE val){  //入队:入队一个元素,队尾向后移动一下
    if(is_full(pQ)){
        printf("队列已满,无法添加元素!\n");
        return false;
    }else{
        pQ->pBase[pQ->rear] = val;
        pQ->rear = (pQ->rear + 1) % MAX_SIZE;
        return true;
    }
}

bool de_queue(QUEUE * pQ, PNODE * val){
    if(pQ->front == pQ->rear){
        printf("该队列为空,无法出队!\n");
        return false;
    }else{
        *val = pQ->pBase[pQ->front];
        pQ->front = (pQ->front + 1) % MAX_SIZE;
        return true;
    }
}

//==============================二叉树功能实现=============================================
/*
使用二级指针的原因:
1.若要通过函数B修改函数A中的某个变量a。需要获得变量a的地址,如果
a是普通变量,需要获得一级指针。如果a是指针,需要获得二级指针。
2.root需要指向一个新插入的节点,也就是需要修改root的值。所以 应该传入指向root的地址。
3.如果仅使用一级指针,那么只能改变形参的内存,如,root = malloc(sizeof(struct TreeNode)); 
这个root和函数外的实参的内存空间不同,所以修改它并不会对实参的内存造成影响,
所以必须把它做为返回值返回去,否则函数外就没法获得这个变化的值。
*/

/*
A.先序建立二叉树(无返回值版本)
需要在main函数中定义一个根节点
注意这是个二叉树节点的指针类型;然后将这个参数
传递给一个CreateBTree函数;在该函数中递归创建二叉树
*/
void CreateBTree(BiTree * root){  //二级指针(BiTree本身就是指针)
    char c;
    scanf("%c", &c);  //接受一个字符
    if(c == '#'){  //遇到#
        *root = NULL;  //将此节点置为空
    }else{  //从函数外部接受root(二级指针的形式)并在函数内部进行修改
        (*root) = (BiTree)malloc(sizeof(BTNode));  //创建一个新的节点
        (*root)->data = c;
        CreateBTree(&(*root)->pLchild);   //沿左子树继续创建
        CreateBTree(&(*root)->pRchild);   //沿右子树继续创建
    }
    
}

/*
B.先序建立二叉树(有返回值版)
可以直接在CreatBiTree函数中创建二叉树,并返回二叉树的根指针
*/
BiTree CreateBTree2(){
    char c;
    BiTree root = NULL;  //在函数内部创建root
    scanf("%c", &c);  //接受一个字符
    if(c == '#'){  //遇到#
        root = NULL;  //将此节点置为空
        return NULL; //必须return NULL
    }else{
        root = (BiTree)malloc(sizeof(BTNode));  //创建一个新的节点
        root->data = c;
        root->pLchild = CreateBTree2();   //沿左子树继续创建
        root->pRchild = CreateBTree2();   //沿右子树继续创建
        return root;
    }
}

//先序遍历二叉树
void PreOrderTraverse(BiTree root){
    if(root != NULL){
        printf("%c", root->data);
        PreOrderTraverse(root->pLchild);
        PreOrderTraverse(root->pRchild);
    }
}

//中序遍历
void InOrderTraverse(BiTree root){
    if(root != NULL){
        InOrderTraverse(root->pLchild);
        printf("%c", root->data);
        InOrderTraverse(root->pRchild);
    }
}

//中序遍历非递归方法(借助栈完成)
void InOrderTraverse2(BiTree root){
    STACK S;  //STACK等价于 struct Stack
    init_S(&S);
    BiTree t = root;
    while(!is_empty(&S) || t != NULL){
        if(t){  //若当前节点不为空则将其入栈,继续向左子树遍历
            push(&S, t);  //入栈的是整个树节点,并非字符
            t = t->pLchild;
        }else{  //若左子树为空,出栈并访问,然后访问其右子树
            pop(&S, &t);
            printf("%c", t->data);
            t = t->pRchild;
        }
    }
}

//后序遍历
void PostOrderTraverse(BiTree root){
    if(root != NULL){
        PostOrderTraverse(root->pLchild);
        PostOrderTraverse(root->pRchild);
        printf("%c", root->data);
    }
}

//层次遍历
void LevelOrder(BiTree root){
    QUEUE Q;  //实例化一个队列
    init_Q(&Q);
    en_queue(&Q, root);  //将根节点入队
    BiTree val;  //保存出队元素
    while(!q_empty(&Q)){
        de_queue(&Q, &val);
        printf("%c", val->data);
        if(val->pLchild != NULL){
            en_queue(&Q, val->pLchild);
        }
        if(val->pRchild != NULL){
            en_queue(&Q, val->pRchild);
        }
    }
}

int main(){
    //A.二级指针构造方式
    // BiTree root = NULL;
    // CreateBTree(&root);
    // PreOrderTraverse(root);
    //B.一级指针构造方式
    BiTree root = CreateBTree2();
    printf("先序遍历二叉树\n");
    PreOrderTraverse(root);
    printf("\n");
    printf("中序遍历二叉树\n");
    InOrderTraverse(root);
    printf("\n");
    printf("后序遍历二叉树\n");
    PostOrderTraverse(root);
    printf("\n");
    printf("非递归中序遍历二叉树\n");
    InOrderTraverse2(root);
    printf("\n层次遍历二叉树\n");
    LevelOrder(root);
    return 0;
}

程序运行:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值