一、定义
二叉树是n (n≥0)个结点的有限集合:
(1)、或者为空二叉树,即n = 0。
(2)、或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。
二、结构
结构示意图:
代码表示:
#define ElemType char //结点内数据的类型
//结点结构
typedef struct BinTreeNode
{
ElemType data; //数据域
struct BinTreeNode *leftChild; //左孩子
struct BinTreeNode *rightChild; //右孩子
}BinTreeNode;
//根结点管理
typedef struct BinTree
{
BinTreeNode *root;//根结点
ElemType refvalue; //stop flag 停止的标记(用来设置停止的记号)
}BinTree;
三、常用操作
初始化
//二叉树初始化
void InitBinTree(BinTree *bt, ElemType ref)
{
bt->root = NULL;//根设置为空
bt->refvalue = ref; //设置停止标记
}
创建
方式一
//方式一:使用二级指针
//供外部调用
void CreateBinTree_1(BinTree *bt)
{
CreateBinTree_1(bt,&(bt->root));
}
//内部实现
void CreateBinTree_1(BinTree *bt, BinTreeNode **t)
{
ElemType Item; //接收数据
scanf("%c",&Item);
if(Item == bt->refvalue) //判断输入是否为一个结束标记
(*t) = NULL;//是 说明这棵二叉树是空树
else
{//否
//创建树(子树)根结点
(*t) = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert((*t) != NULL);
(*t)->data = Item;//为结点赋值
CreateBinTree_1(bt,&((*t)->leftChild)); //创建左子树
CreateBinTree_1(bt,&((*t)->rightChild)); //创建右子树
}
}
方式二
//方式二:使用指针引用方式
//对外调用
void CreateBinTree_2(BinTree *bt)
{
CreateBinTree_2(bt,bt->root);
}
//内部实现
void CreateBinTree_2(BinTree *bt, BinTreeNode *&t)
{
ElemType Item;//接收数据
scanf("%c",&Item);
if(Item == bt->refvalue)//判断输入是否为一个结束标记
t = NULL;//是 说明这棵二叉树是空树
else
{//否
//创建树(子树)根结点
t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert(t != NULL);
t->data = Item;//为结点赋值
CreateBinTree_2(bt,t->leftChild); //创建左子树
CreateBinTree_2(bt,t->rightChild); //创建右子树
}
}
方式三
//方式三:通过返回值返回二叉树
void CreateBinTree_3(BinTree *bt)
{
bt->root = CreateBinTree_3_(bt);
}
BinTreeNode* CreateBinTree_3_(BinTree *bt)
{
ElemType Item; //接收数据
scanf("%c",&Item);
if(Item == bt->refvalue) //判断输入是否为一个结束标记
return NULL;//是 说明这棵二叉树是空树
else
{//否
//创建树(子树)根结点
BinTreeNode *t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert(t != NULL);
t->data = Item;//为结点赋值
t->leftChild = CreateBinTree_3_(bt);//创建左子树
t->rightChild = CreateBinTree_3_(bt);//创建右子树
return t;//返回所创建的树
}
}
方式四
//方式4:传入要创建的二叉树的串进行创建
void CreateBinTree_4(BinTree *bt, char *str)
{
CreateBinTree_4(bt,bt->root,str);
}
void CreateBinTree_4(BinTree *bt, BinTreeNode *&t, char *&str)
{
if(*str == bt->refvalue) //判断输入是否为一个结束标记
t = NULL; //是 说明这棵二叉树是空树
else
{
//创建树(子树)根结点
t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert(t != NULL);
t->data = *str;//为结点赋值
CreateBinTree_4(bt,t->leftChild,++str);//创建左子树
CreateBinTree_4(bt,t->rightChild,++str);//创建右子树
}
}
方式五
//根据二叉树的前序和中序序列创建树
void CreateBinTree_5(BinTree *bt, char *VLR, char *LVR, int n)
{
//n结点个数
CreateBinTree_5(bt->root,VLR,LVR,n);
}
void CreateBinTree_5(BinTreeNode *&t, char *VLR, char *LVR, int n)
{
if(n == 0)
t = NULL;
else
{
int k = 0;
//利用先序序列,查找根在中序序列的位置
while(VLR[0] != LVR[k])
k++;
//创建根
t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert(t != NULL);
t->data = LVR[k];
//创建t的左子树
CreateBinTree_5(t->leftChild,VLR+1,LVR,k);
//创建右子树
CreateBinTree_5(t->rightChild,VLR+k+1,LVR+k+1,n-k-1);
}
}
方式六
//根据中序和后序序列创建二叉树
void CreateBinTree_6(BinTree *bt, char *LVR, char *LRV, int n)
{
CreateBinTree_6(bt->root,LVR,LRV,n);
}
void CreateBinTree_6(BinTreeNode *&t, char *LVR, char *LRV, int n)
{
if(n == 0)
t = NULL;
else
{
//利用后序序列,查找根在中序序列的位置
int k = 0;
while(LRV[n-1] != LVR[k]) //后序需要从后往前找根
k++;
//创建根结点
t = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert(t != NULL);
t->data = LVR[k];
//创建右子树
CreateBinTree_6(t->rightChild,LVR+k+1,LRV+k,n-k-1);
//创建左子树
CreateBinTree_6(t->leftChild,LVR,LRV,k);
}
}
二叉树遍历
1、递归方式
先序遍历
//先序遍历
void PreOrder(BinTree *bt)
{
PreOrder(bt->root);
}
void PreOrder(BinTreeNode *t)
{
if(t != NULL)//树不为空
{
printf("%c ",t->data);//访问(子树)根结点
PreOrder(t->leftChild);//访问左子树
PreOrder(t->rightChild);//访问右子树
}
}
中序遍历
//中序遍历
void InOrder(BinTree *bt)
{
InOrder(bt->root);
}
void InOrder(BinTreeNode *t)
{
if(t != NULL)
{
InOrder(t->leftChild);//访问左子树
printf("%c ",t->data);//访问(子树)根
InOrder(t->rightChild);//访问右子树
}
}
后序遍历
//后序遍历
void PostOrder(BinTree *bt)
{
PostOrder(bt->root);
}
void PostOrder(BinTreeNode *t)
{
if(t != NULL)
{
PostOrder(t->leftChild);//访问左子树
PostOrder(t->rightChild);//访问右子树
printf("%c ",t->data);//访问(子树)根
}
}
2、非递归方式
层次遍历:层次遍历需要借用队列结构,此处给出队列的结构
//队列结构
struct BinTreeNode;
#define EType BinTreeNode*
typedef struct QueueNode
{
EType data;
struct QueueNode *next;
}QueueNode;
typedef struct LinkQueue
{
QueueNode *front;
QueueNode *tail;
}LinkQueue;
下面介绍具体实现
//层次遍历
void LevelOrder(BinTree *bt)
{
LevelOrder(bt->root);
}
void LevelOrder(BinTreeNode *t)
{
if(t != NULL)
{
BinTreeNode *v;
LinkQueue Q; //创建队列
InitQueue(&Q); //对队列初始化
EnQueue(&Q,t);//将二叉树根结点(地址)入队
while(!QueueIsEmpty(&Q)) //判断队列是否为空
{//不空
GetHead(&Q,&v);//取(子树)根结点(地址)
DeQueue(&Q);//将(子树)根结点(地址)出队
printf("%c ",v->data);//打印出(子树)根结点的数据
if(v->leftChild != NULL) //判断该结点的左树是否为空
EnQueue(&Q,v->leftChild); //否 将左子树(地址)入队
if(v->rightChild != NULL) //判断该结点右子树是否为空
EnQueue(&Q,v->rightChild);//否 将右子树(地址)入队
}
}
}
而对于先序遍历、中序遍历和后序遍历的非递归实现需要用到栈结构,下面给出它们的栈结构。
先序遍历和中序遍历非递归形式使用的栈结构
struct BinTreeNode;
#define ET BinTreeNode*
typedef struct SeqStack
{
ET *base;
int capacity;
int top;
}SeqStack;
后序遍历非递归实现使用的栈结构
//栈结构
//左右标记
typedef enum{L,R}Tag;
typedef struct StkNode
{
BinTreeNode *ptr; //保存结点地址
Tag tag; //标记(标记左还是右)
}StkNode;
#define ET StkNode
typedef struct SeqStack
{
ET *base;
int capacity;
int top;
}SeqStack;
下面介绍具体实现:
先序遍历的非递归实现
//先序遍历的非递归实现
void PreOrder_1(BinTree *bt)
{
PreOrder_1(bt->root);
}
void PreOrder_1(BinTreeNode *t)
{
if(t != NULL) //判断二叉树是否为空
{
SeqStack st; //申请一个栈
InitStack(&st); //初始化栈
BinTreeNode *p;
Push(&st,t); //将二叉树的根结点入栈
while(!IsEmpty(&st)) //判断栈顶是否为空
{//否
GetTop(&st,&p);//获取栈顶结点
Pop(&st);//出栈
printf("%c ",p->data);//打印数据
/*这里需要注意的是栈结构是先进后出的,所以后访问的结点先入栈*/
if(p->rightChild != NULL) //判断右子树是否为空
Push(&st,p->rightChild); //否 入栈
if(p->leftChild != NULL) //判断左子树是否为空
Push(&st,p->leftChild); //否 入栈
}
}
}
中序遍历的非递归实现
//中序遍历:非递归实现
void InOrder_1(BinTree *bt)
{
InOrder_1(bt->root);
}
void InOrder_1(BinTreeNode *t)
{
if(t != NULL) //判断二叉树是否为空
{//不空
SeqStack st; //设置一个栈结构
InitStack(&st); //初始化栈
BinTreeNode *p;
Push(&st,t); //将根结点入栈
while(!IsEmpty(&st)) //判断栈是否为空
{//不空
//判断左子树是否为空
while(t!=NULL && t->leftChild!=NULL)
{//不空
Push(&st,t->leftChild);//左子树入栈
t = t->leftChild;//位置下移
}
//此时已经走到了左子树的尽头
//获取栈顶元素
GetTop(&st,&p);
Pop(&st); //出栈
printf("%c ",p->data); //打印元素
//判断右子树是否为空
if(p->rightChild != NULL)
{//不空
t = p->rightChild;//访问右子树
if(t != NULL) //将右子树入栈
Push(&st,t);
}
}
}
}
后序遍历的递归实现
//后序遍历:非递归形式
void PostOrder(BinTree *bt)
{
PostOrder(bt->root);
}
void PostOrder(BinTreeNode *t)
{
if(t != NULL)
{
SeqStack st;
InitStack(&st);
BinTreeNode *p;
StkNode sn;
do
{
while(t != NULL)
{
sn.ptr = t;//指向二叉树树的根结点
sn.tag = L; //刚开始访问左子树,标记为左
Push(&st,sn);//入栈
t = t->leftChild;//指向左子树的根结点
}
bool flag = true; //continue visit
while(flag && !IsEmpty(&st))
{
GetTop(&st,&sn); //获取栈顶元素
Pop(&st); //出栈
p = sn.ptr;
switch(sn.tag) //看结点的标记信息
{
/*
如果标记是左标记,说明接下来还需要访问右子树,不能访问根结点,
如果标记是右标记,那么说明左右子树都已经访问完成可以访问根
*/
case L: //左标记:接下来还需要访问右子树
sn.tag = R; //将左标记改为右
Push(&st,sn); //重新入栈
flag = false;
t = p->rightChild;//接下来要访问右子树
break;
case R: //右标记 可以访问根结点,打印信息
printf("%c ",p->data);
break;
}
}
}while(!IsEmpty(&st));//栈为空时,退出
}
}
统计结点个数
//求以bt为根结点的二叉树结点个数
int Size(BinTree *bt)
{
return Size(bt->root);
}
int Size(BinTreeNode *t)
{
if(t == NULL) //判断二叉树是否为空
return 0;//是 记录个数为0
else//否 该二叉树的结点个数=左子树结点个数+右子树结点个数+根结点个数(1)
return Size(t->leftChild)+Size(t->rightChild)+1;
}
求二叉树高度
//求取二叉树的高度
int Height(BinTree *bt)
{
return Height(bt->root);
}
int Height(BinTreeNode *t)
{
if(t == NULL) //判断二叉树是否为空
return 0;//是 记录高度为0
else
{//否
//求取左子树高度
int left_height = Height(t->leftChild);
//求取右子树高度
int right_height = Height(t->rightChild);
//返回:该二叉树高度=左子树与右子树的最大高度+1
return (left_height>right_height ? left_height:right_height)+1;
}
}
查找结点
//查找key值所在的结点
BinTreeNode* Search(BinTree *bt, ElemType key)
{
return Search(bt->root,key);
}
BinTreeNode* Search(BinTreeNode *t, ElemType key)
{
if(t == NULL) //判断二叉树是否为空
return NULL; //是 说明不存在,返回空
if(t->data == key) //否 判断结点值是否等于key值
return t; //是 返回结点地址
//没找到
BinTreeNode *p = Search(t->leftChild,key); //查找左子树
if(p != NULL)//判断是否找到
return p;//找到 返回结点地址
// 没找到
return Search(t->rightChild,key); // 查找右子树,返回查找结果
}
查找某结点的父结点
//在bt二叉树中查找p结点的父结点
BinTreeNode* Parent(BinTree *bt, BinTreeNode *p)
{
return Parent(bt->root,p);
}
BinTreeNode* Parent(BinTreeNode *t, BinTreeNode *p)
{
if(t==NULL || p==NULL) //判断二叉树和结点是否为空
return NULL;//是 返回空
//否
if(t->leftChild==p || t->rightChild==p)//判断t的左右孩子是否为p
return t;// 是 返回其父结点
//否
BinTreeNode *q = Parent(t->leftChild,p);//查找t的左子树
if(q != NULL)//判断左子树中是否找到满足条件的结点
return q;// 是 返回
//否
return Parent(t->rightChild,p);//查找右子树
}
获取左子树
//获取二叉树的左子树
BinTreeNode* LeftChild(BinTreeNode *p)
{
if(p != NULL)
return p->leftChild;
return NULL;
}
获取右子树
//获取二叉树的右子树
BinTreeNode* RightChild(BinTreeNode *p)
{
if(p != NULL)
return p->rightChild;
return NULL;
}
判断二叉树是否为空
//判断二叉树是否为空
bool BinTreeEmpty(BinTree *bt)
{
return bt->root==NULL;//判断根结点是否为空
}
拷贝二叉树
//拷贝二叉树:bt2拷贝到bt1
void Copy(BinTree *bt1, BinTree *bt2)
{
Copy(bt1->root,bt2->root);
}
void Copy(BinTreeNode *&t1, BinTreeNode *t2)
{
if(t2 == NULL) //判断t2是否为空
t1 = NULL;//是 则无需拷贝,直接将t1置空
else
{//否
//为t2(子树)根结点拷贝到t1(子树)根结点
t1 = (BinTreeNode*)malloc(sizeof(BinTreeNode));
assert(t1 != NULL);
t1->data = t2->data;
Copy(t1->leftChild,t2->leftChild);//拷贝t2左子树到t1左子树
Copy(t1->rightChild,t2->rightChild);//拷贝t2右子树到t1右子树
}
}
清空二叉树
//清空操作
void BinTreeClear(BinTree *bt)
{
BinTreeClear(bt->root);
}
void BinTreeClear(BinTreeNode *&t)
{
if(t != NULL) //判断二叉树是否为空
{//否
BinTreeClear(t->leftChild);//清空左子树
BinTreeClear(t->rightChild);//清空右子树
free(t);//清空(子树)根
t = NULL;
}
}
结语
对二叉树的介绍就到这里啦,希望这篇文章能给予你一些帮助,感谢各位人才的:点赞、收藏和评论,我们下次见。
附录
测试代码:二叉树详解(C语言版)