二叉树和相关操作--c语言

本文详细介绍了二叉树的链式存储结构,包括先序、中序、后序和层次遍历的递归实现,以及线索二叉树的中序、先序和后序线索化方法,用于高效遍历。同时,还探讨了二叉排序树的查找和插入操作。所有代码均提供了清晰的实现。
摘要由CSDN通过智能技术生成

•二叉树的链式存储结构

typedef struct BiTNode{
    ElemType data;  //数据域
    struct BiTNode *lchild;    //左子树结点指针
    struct BiTNode *rchild;    //右子树结点指针
    //添加根结点指针,构成二叉树的三叉链表
    //struct BiTNode *parent;
}BiTNode,*BiTree;

 
 

•二叉树的遍历

1.先序遍历

//先序遍历(前缀表达式)
void PreOrder(BiTree T){
    if(T != NULL){
        visit(T);  //访问根节点
        PreOrder(T->lchild);  //访问左节点
        PreOrder(T->rchild);  //访问右节点
    }
}

 

2.中序遍历

//中序遍历(中缀表达式)
void InOrder(BiTree T){
    if(T != NULL){
        PreOrder(T->lchild);  //访问左节点
        visit(T);  //访问根节点
        PreOrder(T->rchild);  //访问右节点
    }
}

 

3.中序遍历

//后序遍历(后缀表达式)
void PostOrder(BiTree T){
    if(T != NULL){
        PreOrder(T->lchild);  //访问左节点
        PreOrder(T->rchild);  //访问右节点
        visit(T);  //访问根节点
    }
}

利用递归工作栈进行的三种顺序遍历每个结点都访问且仅访问一次,时间复杂度都是 O ( n ) O(n) O(n)
在最坏情况下,二叉树有n个结点且深度为n,此时空间复杂度为 O ( n ) O(n) O(n)

 

4.层次遍历

这里用到的队列相关操作见 《队列的相关操作》

//层次遍历
void LevelOrder(BiTree T){
    LinkQueue Q;  //创建一个辅助队列
    INitQueue(Q);  //初始化队列
    BiTree p;  //存储需要访问的出队结点
    EnQueue(Q,T); //根节点入队
    while(!ISEmpty(Q)){
        //队列非空时,进行循环
        DeQueue(Q,p);  //队头结点出队
        visit(p);  //访问出队结点
        if(p->lchild != NULL){
            EnQueue(Q,p->lchild);  //出队结点的左孩子入队
        }
        if(p->rchild != NULL){
            EnQueue(Q,p->rchild);  //出队结点的右孩子入队
        }
    }
}

 

5.代码汇总

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

#define ElemType int

//二叉树的链式存储结点
typedef struct BiTNode{
    ElemType data;  //数据域
    struct BiTNode *lchild;    //左子树结点指针
    struct BiTNode *rchild;    //右子树结点指针
    //添加根结点指针,构成二叉树的三叉链表
    //struct BiTNode *parent;
}BiTNode,*BiTree;


//-------------------------------------------------------------------
//一个任意的访问函数
void visit(BiTree T) {
    /*
     * 编写相应的访问函数
     */
}
//-------------------------------------------------------------------


//-------------------------------------------------------------------
//先序遍历(前缀表达式)
void PreOrder(BiTree T){
    if(T != NULL){
        visit(T);  //访问根节点
        PreOrder(T->lchild);  //访问左节点
        PreOrder(T->rchild);  //访问右节点
    }
}

//中序遍历(中缀表达式)
void InOrder(BiTree T){
    if(T != NULL){
        PreOrder(T->lchild);  //访问左节点
        visit(T);  //访问根节点
        PreOrder(T->rchild);  //访问右节点
    }
}

//后序遍历(后缀表达式)
void PostOrder(BiTree T){
    if(T != NULL){
        PreOrder(T->lchild);  //访问左节点
        PreOrder(T->rchild);  //访问右节点
        visit(T);  //访问根节点
    }
}

//层次遍历
void LevelOrder(BiTree T){
    LinkQueue Q;  //创建一个辅助队列
    INitQueue(Q);  //初始化队列
    BiTree p;  //存储需要访问的出队结点
    EnQueue(Q,T); //根节点入队
    while(!ISEmpty(Q)){
        //队列非空时,进行循环
        DeQueue(Q,p);  //队头结点出队
        visit(p);  //访问出队结点
        if(p->lchild != NULL){
            EnQueue(Q,p->lchild);  //出队结点的左孩子入队
        }
        if(p->rchild != NULL){
            EnQueue(Q,p->rchild);  //出队结点的右孩子入队
        }
    }
}
//-------------------------------------------------------------------

 
 

•线索二叉树

//线索二叉树结点
typedef struct ThreadNode{
    ElemType data;  //数据域
    struct ThreadNode *lchild;    //左子树结点指针
    struct ThreadNode *rchild;    //右子树结点指针
    bool ltag;  //线索判断左右指针是线索还是孩子结点
    bool rtag;  //true为线索,false为孩子结点
}ThreadNode,*ThreadTree;

 

1.中序线索化

通过中序遍历进行线索化

//通过中序遍历进行的中序线索化
void InThread(ThreadNode *p, ThreadNode *pre){
    //结点非空才能进行线索化
    if(p != NULL){
        //对左孩子结点进行线索化
        InThread(p->lchild,pre);
        //对根结点进行线索化
        if(p->lchild == NULL){
            //左孩子为空,则建立前驱线索
            p->lchild = pre;
            p->ltag = true;
        }
        if(pre != NULL && pre->rchild == NULL){
            //前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
            pre->rchild = p;
            pre->rtag = true;
        }
        pre = p;  //更新前驱结点为当前结点
        //对右孩子结点进行线索化
        InThread(p->rchild,pre);
    }
}

void CreateInThread(ThreadTree T){
    ThreadNode *pre = NULL;  //指向被访问结点的前驱结点
    if(T != NULL){
        //非空二叉树才能进行线索化
        InThread(T,pre);  //中序线索化
        //处理最后一个结点
        if(pre->rchild == NULL){
            pre->rtag = 1;
        }
    }
}

 

中序线索二叉树的遍历

//求中序序列下的第一个结点
ThreadNode *FirestNode(ThreadNode *p){
    while(p->ltag == false){
        //当前结点有左孩子,则往前继续找
        p = p->lchild;
    }
    return p;
}

//求当前结点的中序后继
ThreadNode *NextNode(ThreadNode *p){
    if(p->rtag == false){
        //当前结点有右子树,则返回右子树的第一个结点
        return FirestNode(p->rchild);
    }else{
        //当前结点右子树为空,直接返回后继结果
        return p->rchild;
    }
}

由上面两个函数,可以构成不含头结点的中序线索二叉树的中序遍历算法

//中序线索二叉树的中序遍历
void ThreadInOrder(ThreadTree T){
    for(ThreadNode *p = FirestNode(T); p != NULL; p = NextNode(p)){
        //创建结点p初始化为T树的第一个结点,逐次访问其后继
        visit(p);
    }
}

 

2.先序线索化

大致操作与中序相同,修改各个部分线索化顺序即可

//后序线索二叉树
//通过先序遍历进行的先序线索化
void PreThread(ThreadNode *p, ThreadNode *pre){
    //结点非空才能进行线索化
    if(p != NULL){

        //对根结点进行线索化
        if(p->lchild == NULL){
            //左孩子为空,则建立前驱线索
            p->lchild = pre;
            p->ltag = true;
        }
        if(pre != NULL && pre->rchild == NULL){
            //前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
            pre->rchild = p;
            pre->rtag = true;
        }
        pre = p;  //更新前驱结点为当前结点

        //对左孩子结点进行线索化
        if(p->ltag == false){
            //左子树不是其前驱线索时,才进行线索化
            PreThread(p->lchild,pre);
        }

        //对右孩子结点进行线索化
        PreThread(p->rchild,pre);
    }
}
void CreatePreThread(ThreadTree T){
    ThreadNode *pre = NULL;  //指向被访问结点的前驱结点
    if(T != NULL){
        //非空二叉树才能进行线索化
        PreThread(T,pre);  //中序线索化
        //处理最后一个结点
        if(pre->rchild == NULL){
            pre->rtag = 1;
        }
    }
}

 

3.后序线索化

//后序线索二叉树
//通过后序遍历进行的后序线索化
void PostThread(ThreadNode *p, ThreadNode *pre){
    //结点非空才能进行线索化
    if(p != NULL){

        //对左孩子结点进行线索化
        PostThread(p->lchild,pre);

        //对右孩子结点进行线索化
        PostThread(p->rchild,pre);

        //对根结点进行线索化
        if(p->lchild == NULL){
            //左孩子为空,则建立前驱线索
            p->lchild = pre;
            p->ltag = true;
        }
        if(pre != NULL && pre->rchild == NULL){
            //前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
            pre->rchild = p;
            pre->rtag = true;
        }
        pre = p;  //更新前驱结点为当前结点
    }
}
void CreatePostThread(ThreadTree T){
    ThreadNode *pre = NULL;  //指向被访问结点的前驱结点
    if(T != NULL){
        //非空二叉树才能进行线索化
        PostThread(T,pre);  //中序线索化
        //处理最后一个结点
        if(pre->rchild == NULL){
            pre->rtag = 1;
        }
    }
}

 

4.代码汇总

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

#define ElemType int

//线索二叉树结点
typedef struct ThreadNode{
    ElemType data;  //数据域
    struct ThreadNode *lchild;    //左子树结点指针
    struct ThreadNode *rchild;    //右子树结点指针
    bool ltag;  //线索判断左右指针是线索还是孩子结点
    bool rtag;  //true为线索,false为孩子结点
}ThreadNode,*ThreadTree;


//-------------------------------------------------------------------
//中序线索二叉树
//通过中序遍历进行的中序线索化
void InThread(ThreadNode *p, ThreadNode *pre){
    //结点非空才能进行线索化
    if(p != NULL){
        //对左孩子结点进行线索化
        InThread(p->lchild,pre);
        //对根结点进行线索化
        if(p->lchild == NULL){
            //左孩子为空,则建立前驱线索
            p->lchild = pre;
            p->ltag = true;
        }
        if(pre != NULL && pre->rchild == NULL){
            //前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
            pre->rchild = p;
            pre->rtag = true;
        }
        pre = p;  //更新前驱结点为当前结点
        //对右孩子结点进行线索化
        InThread(p->rchild,pre);
    }
}
void CreateInThread(ThreadTree T){
    ThreadNode *pre = NULL;  //指向被访问结点的前驱结点
    if(T != NULL){
        //非空二叉树才能进行线索化
        InThread(T,pre);  //中序线索化
        //处理最后一个结点
        if(pre->rchild == NULL){
            pre->rtag = 1;
        }
    }
}

//求中序序列下的第一个结点
ThreadNode *FirestNode(ThreadNode *p){
    while(p->ltag == false){
        //当前结点有左孩子,则往前继续找
        p = p->lchild;
    }
    return p;
}
//求当前结点的中序后继
ThreadNode *NextNode(ThreadNode *p){
    if(p->rtag == false){
        //当前结点有右子树,则返回右子树的第一个结点
        return FirestNode(p->rchild);
    }else{
        //当前结点右子树为空,直接返回后继结果
        return p->rchild;
    }
}
//中序线索二叉树的中序遍历
void ThreadInOrder(ThreadTree T){
    for(ThreadNode *p = FirestNode(T); p != NULL; p = NextNode(p)){
        //创建结点p初始化为T树的第一个结点,逐次访问其后继
        visit(p);
    }
}
//-------------------------------------------------------------------


//-------------------------------------------------------------------
//先序线索二叉树
//通过先序遍历进行的先序线索化
void PreThread(ThreadNode *p, ThreadNode *pre){
    //结点非空才能进行线索化
    if(p != NULL){

        //对根结点进行线索化
        if(p->lchild == NULL){
            //左孩子为空,则建立前驱线索
            p->lchild = pre;
            p->ltag = true;
        }
        if(pre != NULL && pre->rchild == NULL){
            //前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
            pre->rchild = p;
            pre->rtag = true;
        }
        pre = p;  //更新前驱结点为当前结点

        //对左孩子结点进行线索化
        if(p->ltag == false){
            //左子树不是其前驱线索时,才进行线索化
            PreThread(p->lchild,pre);
        }

        //对右孩子结点进行线索化
        PreThread(p->rchild,pre);
    }
}
void CreatePreThread(ThreadTree T){
    ThreadNode *pre = NULL;  //指向被访问结点的前驱结点
    if(T != NULL){
        //非空二叉树才能进行线索化
        PreThread(T,pre);  //中序线索化
        //处理最后一个结点
        if(pre->rchild == NULL){
            pre->rtag = 1;
        }
    }
}
//-------------------------------------------------------------------


//-------------------------------------------------------------------
//后序线索二叉树
//通过后序遍历进行的后序线索化
void PostThread(ThreadNode *p, ThreadNode *pre){
    //结点非空才能进行线索化
    if(p != NULL){

        //对左孩子结点进行线索化
        PostThread(p->lchild,pre);

        //对右孩子结点进行线索化
        PostThread(p->rchild,pre);

        //对根结点进行线索化
        if(p->lchild == NULL){
            //左孩子为空,则建立前驱线索
            p->lchild = pre;
            p->ltag = true;
        }
        if(pre != NULL && pre->rchild == NULL){
            //前驱结点非空,且前驱无右孩子,则建立前驱右孩子的后继线索
            pre->rchild = p;
            pre->rtag = true;
        }
        pre = p;  //更新前驱结点为当前结点
    }
}
void CreatePostThread(ThreadTree T){
    ThreadNode *pre = NULL;  //指向被访问结点的前驱结点
    if(T != NULL){
        //非空二叉树才能进行线索化
        PostThread(T,pre);  //中序线索化
        //处理最后一个结点
        if(pre->rchild == NULL){
            pre->rtag = 1;
        }
    }
}
//-------------------------------------------------------------------

 
 

•二叉排序树

 

1.查找

有递归与非递归两种

//非递归查找
BSTNode *BST_Search(BSTree T, ElemType key) {
    while (T != NULL && key != T->data) {
        //当树结点T非空且T值不是key值时进行循环判定
        if (key < T->data) {
            T = T->lchild;  //key值比T值小,继续向T的左子树寻找
        }else{
            T = T->rchild;  //key值比T值大,继续向T的右子树寻找
        }
    }
    return T;  //返回符合key的结点T,没有找到则T为空
}
//递归查找
BSTNode *BST_Search1(BSTree T, ElemType key){
    if(T == NULL){
        return NULL;  //查找失败
    }
    if(key == T->data){
        return T;  //查找成功
    }
    if (key < T->data) {
        return BST_Search1(T->lchild, key);  //key值比T值小,继续向T的左子树寻找
    }
    if(key > T->data){
        return BST_Search1(T->rchild, key);  //key值比T值大,继续向T的右子树寻找
    }
}

 

2.插入

和查找的方法差不多,也是查找找到正确位置以后增加一个插入操作
循环进行插入操作,就可以构建一个二叉排序树

//非递归插入
bool BST_Insert(BSTree T, ElemType key) {
    while (T != NULL && key != T->data) {
        //当树结点T非空,则继续移动
        if (key < T->data) {
            T = T->lchild;  //key值比T值小,继续向T的左子树移动
        } else {
            T = T->rchild;  //key值比T值大,继续向T的右子树移动
        }
    }
    if(key == T->data){
        return false;  //存在关键字相同的结点,插入失败
    }else{
        //找到正确位置的空结点T以后,进行插入操作
        T = (BSTree) malloc(sizeof(BSTNode));
        T->data = key;
        T->lchild = NULL;
        T->rchild = NULL;
        return true;
    }
}
//递归插入
bool BST_Insert1(BSTree T, ElemType key) {
    if (T == NULL) {
        T = (BSTree) malloc(sizeof(BSTNode));
        T->data = key;
        T->lchild = NULL;
        T->rchild = NULL;
        return true;
    }
    if (key == T->data) {
        return false;  //存在关键字相同的结点,插入失败
    }
    if (key < T->data) {
        return BST_Insert1(T->lchild, key);  //key值比T值小,继续向T的左子树插入
    }
    if (key > T->data) {
        return BST_Insert1(T->rchild, key);  //key值比T值大,继续向T的右子树插入
    }
}

 

3.代码汇总

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

#define ElemType int

//二叉排序树的链式存储结点
typedef struct BSTNode {
    ElemType data;  //数据域
    struct BSTNode *lchild, *rchild;    //左右子树结点指针
} BSTNode, *BSTree;

//------------------------------------------------------------------
//非递归查找
BSTNode *BST_Search(BSTree T, ElemType key) {
    while (T != NULL && key != T->data) {
        //当树结点T非空且T值不是key值时进行循环判定
        if (key < T->data) {
            T = T->lchild;  //key值比T值小,继续向T的左子树寻找
        } else {
            T = T->rchild;  //key值比T值大,继续向T的右子树寻找
        }
    }
    return T;  //返回符合key的结点T,没有找到则T为空
}

//递归查找
BSTNode *BST_Search1(BSTree T, ElemType key) {
    if (T == NULL) {
        return NULL;  //查找失败
    }
    if (key == T->data) {
        return T;  //查找成功
    }
    if (key < T->data) {
        return BST_Search1(T->lchild, key);  //key值比T值小,继续向T的左子树寻找
    }
    if (key > T->data) {
        return BST_Search1(T->rchild, key);  //key值比T值大,继续向T的右子树寻找
    }
}
//------------------------------------------------------------------


//------------------------------------------------------------------
//非递归插入
bool BST_Insert(BSTree T, ElemType key) {
    while (T != NULL && key != T->data) {
        //当树结点T非空,则继续移动
        if (key < T->data) {
            T = T->lchild;  //key值比T值小,继续向T的左子树移动
        } else {
            T = T->rchild;  //key值比T值大,继续向T的右子树移动
        }
    }
    if(key == T->data){
        return false;  //存在关键字相同的结点,插入失败
    }else{
        //找到正确位置的空结点T以后,进行插入操作
        T = (BSTree) malloc(sizeof(BSTNode));
        T->data = key;
        T->lchild = NULL;
        T->rchild = NULL;
        return true;
    }
}
//递归插入
bool BST_Insert1(BSTree T, ElemType key) {
    if (T == NULL) {
        T = (BSTree) malloc(sizeof(BSTNode));
        T->data = key;
        T->lchild = NULL;
        T->rchild = NULL;
        return true;
    }
    if (key == T->data) {
        return false;  //存在关键字相同的结点,插入失败
    }
    if (key < T->data) {
        return BST_Insert1(T->lchild, key);  //key值比T值小,继续向T的左子树插入
    }
    if (key > T->data) {
        return BST_Insert1(T->rchild, key);  //key值比T值大,继续向T的右子树插入
    }
}
//------------------------------------------------------------------
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值