树的定义
树的特点
树的模型
#二叉树
二叉树是另一种树形结构,其特点是每个节点最多有两个子树,并且二叉树的子树有左右之分,其次序不能任意颠倒。与树相似,二叉树也是以递归的形式定义(即每次定义时候的操作是重复的即为根节点,左子树,右子树,然后左子树为根节点再次重复操作),当集合中的节点数为0时,为空二叉树,或者由一个根节点和两个互不相交的被称为根的左子树和右子树组成,左子树和右子树分别是一棵二叉树
二叉树的特例
满二叉树和完全二叉树
满二叉树即为每个父节点的左右子树都不为空,而完全二叉树的定义为右边可以为空但是左边不能为空,例如去除完全二叉树的第十一个节点,这个时候就不叫作二叉树,但是如果保留第十一个节点,去除第十二个节点这个时候还是完全二叉树。
树的抽象数据结构定义
//创建树的结点
typedef struct BiTnode{
Elemtype c;
struct BiTnode *Lchild; //左子树
struct BiTnode *Rchild; //右子树
}BiTnode, *BiTree;
#二叉树层次建树的实现
在上面树的模型那个图片中提到了树的层数,而二叉树的层次创建就是一层一层创建二叉树,如当你存储元素为字符类型时,创建二叉树存储abcdefg的过程就为
1.先将根节点放入树中,将根节点入队,当创建第二个节点时候,判断左右子树是否为空,如果左子树为空,将结点放入根节点的左子树,如果左子树满,右子树空,那么就将结点放入右子树中,在放入根节点的左右子树的同时也要将结点入队,当根节点满的时候,队列中访问根节点的指针就可以移动到下一个结点,这个结点就成为了新的根节点,在队列中存储的结点都可以作为根节点存储左右子树,这样就创建好了一颗二叉树,这也就是二叉树的层次创建,主要就是利用了队列尾插的特性以及二叉树是由一颗颗小的子树组成的特性。
代码实现:
#include<stdio.h>
#include <cstdlib>
//完成二叉树层次建树的实现
typedef char Elemtype;
//创建树的结点
typedef struct BiTnode{
Elemtype c;
struct BiTnode *Lchild; //左子树
struct BiTnode *Rchild; //右子树
}BiTnode, *BiTree;
//辅助队列的作用,帮助判断父节点元素左右子树是否为空
//定义辅助队列结构体 用链表实现
typedef struct tag{
BiTree p; //父节点元素的地址
struct tag *next;
}tag_t,*ptag_t;
int main() {
//第一步创建树
BiTree tree = NULL;
//创建结点指针
BiTnode *bp = NULL;
//创建辅助队列指针
ptag_t head = NULL,tail = NULL, pcur = NULL,listpnew = NULL; //定义辅助队列的头指针与尾指针,先进先出
char c;
while (scanf("%c",&c))
{
if(c == '\n')
{
break;
} else{
listpnew = (tag_t *) calloc(1,sizeof (tag_t));
bp = (BiTnode*) calloc(1,sizeof (BiTnode)); //calloc函数在申请的时候会自动初始化结构体内容为0
bp->c = c;
//判断树是否为空 如果树为空将第一个结点作为根节点
if(tree == NULL)
{
tree = bp;
head = listpnew;//队列中第一个结点作为头节点
tail = head;
head->p = bp;//将根节点放入头节点
pcur = head;
} else{
listpnew->p = bp;
tail->next = listpnew;
tail = listpnew;
tail->next = NULL;
//若左子树为空
if(pcur->p->Lchild == NULL)
{
pcur->p->Lchild = tail->p;
} else if(pcur->p->Rchild == NULL)
{
pcur->p->Rchild = tail->p;
pcur = pcur->next; //此时二叉树父节点放满 pcur进入下个父节点
}
}
}
}
return 0;
}
下面只介绍二叉树的三种常见的遍历方式,先序遍历,中序遍历,后序遍历。他遍历的方式于创建树递归的方式相同,都是根,左,右,
这里只介绍先序遍历二叉树,另外两种二叉树与此种方法思想相同,二叉树的遍历其实是分成每个子树的遍历,子树的遍历顺序为根,左,右, 由于先序遍历的过程为访问根节点,先序遍历左子树,先序遍历右子树,所以每次按照这种顺序的时候,左右子树就可以作为结点进行先序遍历,那么再每次访问完根节点之后,这种方法一定会先去遍历所有的左子树,当查找左子树结点为NULL时递归返回,再去依次遍历右子树。
具体代码实现如下
//先序遍历 每棵子树的遍历顺序都是根左右
void PreQuery(BiTree tree)
{
if(tree != NULL)
{
printf("%c",tree->c);
PreQuery(tree->Lchild);
PreQuery(tree->Rchild);
}
}
//中序遍历 每棵子树的遍历顺序都是左根右
void MidQuery(BiTree tree)
{
if(tree != NULL)
{
PreQuery(tree->Lchild);
printf("%c",tree->c);
PreQuery(tree->Rchild);
}
}
//后序遍历 每棵子树的遍历顺序都是左右根
void EndQuery(BiTree tree)
{
if(tree != NULL)
{
PreQuery(tree->Lchild);
PreQuery(tree->Rchild);
printf("%c",tree->c);
}
}
#二叉树的层次遍历
二叉树的层次遍历就是从第一层开始,从左到右将所有结点值给打印出来,实现过程如下,每个结点出队的时候会将结点值打印,因此根据辅助先进先出的特性,就可以实现对二叉树的层次遍历
//二叉树的层次遍历
void LevelOrder(BiTree tree)
{
LinkQueque Queque; //辅助队列
BiTree outp; //保存出队结点,出队相当于打印
InitQueque2(Queque);
EnQueque2(Queque,tree); //根结点入队
while (!IsEmpty(Queque))
{
DeQueque(Queque,outp); //出队
putchar(outp->c); //打印出队值
if(outp->Lchild!=NULL) // 如果根节点左结点错在,那么左节点入队
{
EnQueque2(Queque,outp->Lchild);
} else if(outp->Rchild != NULL) // 如果右节点左结点错在,那么左节点入队
{
EnQueque2(Queque,outp->Rchild);
}
}
}