6.1 树
6.1.1 树的定义
树是n(n≥0)个结点的有限集合. 当n=0时,称为空树;任意一棵非空树满足一下条件:
①有且仅有一个特定的称为根(root)的结点.
②当n>1时,除根结点之外的其余节点被分成m(m>0)个互不相交的有限集合T1,T2,T3,····,Tm,
其中每个集合又是一棵树,并称为这个根节点的子树(subtree).
6.2树的基本术语
6.2.1 结点的分类(结点的度,树的度,叶子节点,分支节点)
6.2.2 节点间的关系(孩子节点,双亲节点,兄弟节点,祖先,子孙)
6.2.3其他关系(结点的层数,树的深度或高度,有序树,无序树,森林)
线性结构与树结构比较:
6.3 树的存储结构
详细解释: 点击打开链接
6.4 二叉树
6.4.1 二叉树的定义
二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根节点和
两棵互不相交的、分别称为根结点的左子树(left subtree)和右子树(right subtree)的二叉树组成.
二叉树具有5种基本形态:
①空二叉树
②只有一个根结点
③根结点只有左子树
④根结点只有右子树
⑤根结点既有左子树又有右子树
6.4.2 特殊二叉树
1.斜树
所有的结点都只有左子树的二叉树叫左斜树.
所有的结点都只有右子树的二叉树叫右子树.
这两者统称为斜树.
2.满二叉树
满二叉树的特点:
①叶子只能出现在最下一层.
②只有度为0和度为2的结点.
3.完全二叉树
完全二叉树的特点:
①叶子结点只能出现在最下两层,且最下层的叶子结点都集中在二叉树左侧连续位置.
②如果有度为1的结点,只可能有一个,且该结点只有左孩子.
6.4.3 二叉树的基本性质
性质 3: 在一棵二叉树中,如果叶子结点的个数为n0,度为2的结点个数为n2,
则 n0=n2+1 .
6.4.4 二叉树的实现
LinkBiTree.h
#ifndef LINKBITREE_H
#define LINKBITREE_H
#include <stdio.h>
#include <stdlib.h>
typedef char DataType;
typedef struct BiNode
{
DataType data;
struct BiNode *lchild,*rchild;
}BiNode,*BiTree;
typedef struct SeqQueue
{
int *data;
int front;
int rear;
}SeqQueue;
BiNode* InitBiTree(BiTree *bt); //初始化二叉树
void PreOrder(BiTree *bt); //前序遍历
void InOrder(BiTree *bt); //中序遍历
void PostOrder(BiTree *bt); //后序遍历
void LeverOrder(BiTree *bt,SeqQueue *Q); //层序遍历
void Release(BiTree *bt); //释放二叉树
#endif
LinkBiTree.c
#include "LinkBiTree.h"
const QueueSize = 20;
/*以前序遍历的方式创建一棵二叉树*/
BiNode* InitBiTree(BiTree *bt)
{
char ch;
printf("Please Enter Data:\n");
scanf("%c",&ch);
getchar(); //去除‘\n'干扰
//printf("ch:%c\n",ch);
if(ch == ' ') //以‘ ’空格为结束标志
(*bt) = NULL;
else
{
*bt = (BiNode *)malloc(sizeof(BiNode));
(*bt)->data=ch;
(*bt)->lchild = InitBiTree(&((*bt)->lchild));
(*bt)->rchild = InitBiTree(&((*bt)->rchild));
}
return *bt;
}
void PreOrder(BiTree *bt)
{
if((*bt) == NULL)
return ;
else
{
printf("%c\n",(*bt)->data);
PreOrder(&(*bt)->lchild);
PreOrder(&(*bt)->rchild);
}
}
void InOrder(BiTree *bt)
{
if(*bt == NULL)
return ;
else
{
InOrder(&(*bt)->lchild);
printf("%c\n",(*bt)->data);
InOrder(&(*bt)->rchild);
}
}
void PostOrder(BiTree *bt)
{
if(*bt == NULL)
return ;
else
{
PostOrder(&(*bt)->lchild);
PostOrder(&(*bt)->rchild);
printf("%c\n",(*bt)->data);
}
}
void LeverOrder(BiTree *bt,SeqQueue *Q)
{
BiNode *p;
//初始化一个队列
Q->data = (int *)malloc(QueueSize*sizeof(int));
if(Q->data == NULL)
return ;
//初始化空队列
Q->front = Q->rear = QueueSize - 1;
/*如果二叉树为NULL,算法结束*/
if((*bt) == NULL)
return ;
/*判断队列是否满*/
if(Q->front == (Q->rear + 1)%QueueSize)
{
printf("Queue is full!\n");
return ;
}
/*将树的根指针入队,由于队列data为int类型,因此将根指针强制类型转化为int型*/
Q->data[++Q->rear]=(int)*bt;
//printf("aaaaaa:%c\n",**bt);
while(Q->front != Q->rear) //队列不为NULL
{
//出队
//intf("aaaaaa:%c\n",Q->data[Q->front]);
p=(BiNode*)Q->data[++Q->front];
printf("%c\n",p->data);
if(p->lchild != NULL) //左孩子入队
Q->data[++Q->rear] = (int)p->lchild;
if(p->rchild != NULL) //右孩子入队
Q->data[++Q->rear] = (int)p->rchild;
}
}
void Release(BiTree *bt)
{
if(*bt != NULL)
{
Release(&(*bt)->lchild);
Release(&(*bt)->rchild);
free(*bt);
}
}
main.c
#include "LinkBiTree.h"
int main(void)
{
BiTree B;
//初始化一棵扩展二叉树'AB D C ';
InitBiTree(&B);
//前序遍历
printf("前序遍历:\n");
PreOrder(&B);
//中序遍历
printf("中序遍历:\n");
InOrder(&B);
//后序遍历
printf("后序遍历:\n");
PostOrder(&B);
SeqQueue Q;
//层序遍历
printf("层序遍历:\n");
LeverOrder(&B,&Q);
//释放二叉树
Release(&B);
return 0;
}
6.4.5 二叉树的非递归实现
LinkBiTree.h
#ifndef LINKBITREE_H
#define LINKBITREE_H
#include <stdio.h>
#include <stdlib.h>
typedef char DataType;
typedef struct BiNode
{
DataType data;
struct BiNode *lchild,*rchild;
}BiNode,*BiTree;
//队列用于二叉树层序遍历
typedef struct SeqQueue
{
int *data;
int front;
int rear;
}SeqQueue;//栈用于非递归实现
typedef struct SeqStack
{
int *data;
int top;
}SeqStack;
BiNode* InitBiTree(BiTree *bt,SeqStack *s); //初始化二叉树
void PreOrder(BiTree *bt,SeqStack *s); //前序遍历
void InOrder(BiTree *bt,SeqStack *s); //中序遍历
void PostOrder(BiTree *bt,SeqStack *s); //后序遍历
void LeverOrder(BiTree *bt,SeqQueue *Q); //层序遍历
void Release(BiTree *bt,SeqStack *s); //释放二叉树
#endif
LinkBiTree.c
#include "LinkBiTree.h"
const QueueSize = 20;
const StackSize = 20;
/*以前序遍历的方式创建一棵二叉树*/
BiNode* InitBiTree(BiTree *bt,SeqStack *s)
{
/*为二叉链表*bt申请空间,并将其赋值给p*/
*bt = (BiTree)malloc(sizeof(BiNode));
BiNode *p=*bt;
BiNode *q;
/*设置左右子树标志*/
int tag[20];
//初始化栈
s->data = (int *)malloc(StackSize*sizeof(int));
s->top = -1;
/*以‘@’为结束标志*/
while(1)
{
char ch;
printf("Please Enter Data:\n");
scanf("%c",&ch);
getchar(); //去除‘\n'干扰
/*ch等于‘@’表示循环结束*/
if(ch=='@')
{
break;
}
if(ch == ' ' )
{
/*如果tag为1,表示p此时应处于左子树位置,设置其左子树为NULL*/
if(tag[s->top]==1)
{
p->lchild = NULL;
}
/*如果tag为2,表示p此时应处于右子树位置,设置其右子树为NULL*/
if(tag[s->top]==2)
{
p->rchild = NULL;
}
// printf("NULL\n");
}
if(ch != ' ')
{
/*将ch赋值给q*/
q=(BiNode *)malloc(sizeof(BiNode));
if(q == NULL) //判断申请内存是否成功
printf("Error!\n");
q->data=ch;
/*top=-1,说明此时输入的时根结点*/
if(s->top==-1)
{
p->data=ch; //根结点直接赋值
printf("root:%c \n",p->data);
}
/*tag == 1,说明此时要添加p的左子树,采用尾插法*/
if(tag[s->top] == 1)
{
p->lchild = q; //将新结点q赋值给p的左子树
p=q; //p指针后移到q
//printf("TopData:%c\n",((BiNode*)s->data[s->top])->data);
//printf("lchild:%c \n",p->data);
//printf("top:%d\n",s->top);
}
/*tag == 2,说明此时要添加p的右子树,采用尾插法*/
if(tag[s->top] == 2)
{
p->rchild = q; //将新结点q赋值给p的右子树
p=q; //p指针后移到q
//printf("TopData:%c\n",((BiNode*)s->data[s->top])->data);
//printf("rchild:%c \n",p->data);
//printf("top:%d\n",s->top);
}
/*将p入栈,将标志tag设为1*/
s->data[++s->top]=(int)(p); //注意此处入栈的是p
tag[s->top]=1;
}else{
/*当栈非空,且tag为2时,出栈*/
while(s->top != -1 && tag[s->top]==2)
{
printf("kkkk:%c\n",((BiNode *)s->data[s->top--])->data);
//printf("top:%d\n",s->top);
}
/*如果栈不为空,将栈顶标志tag设为2,并将栈顶元素赋值给p*/
if(s->top != -1)
{
tag[s->top]=2;
p=((BiNode *)s->data[s->top]);
//printf("mmtop:%c\n",p->data);
}
}
}
return p;
}
//前序遍历非递归实现
void PreOrder(BiTree *bt,SeqStack *s)
{
BiNode *p=*bt;
/*初始化栈*/
s->data = (int *)malloc(StackSize*sizeof(int));
s->top = -1;
/*循环直到bt为空且栈s为空*/
while(p != NULL || s->top != -1)
{
while(p != NULL)
{
printf("%c \n",p->data); //输出
s->data[++(s->top)]=(int)p; //将*bt入栈
p=p->lchild; //继续遍历*bt的左子树
}
if(s->top != -1)
{
p = (BiNode *)s->data[(s->top)--]; //将栈顶元素弹出
p = p->rchild; //准备遍历*bt的右子树
}
}
}
//中序遍历非递归实现
void InOrder(BiTree *bt,SeqStack *s)
{
BiNode *p=*bt;
/*初始化栈*/
s->data = (int *)malloc(StackSize*sizeof(int));
s->top = -1;
/*循环直到bt为空且栈s为空*/
while(p != NULL || s->top != -1)
{
while(p != NULL)
{
s->data[++(s->top)]=(int)p; //将*bt入栈
p=p->lchild; //继续遍历*bt的左子树
}
if(s->top != -1)
{
p = (BiNode *)s->data[(s->top)--]; //将栈顶元素弹出
printf("%c \n",p->data); //输出
p = p->rchild; //准备遍历*bt的右子树
}
}
}
//后序遍历非递归实现
void PostOrder(BiTree *bt,SeqStack *s)
{
BiNode *p=*bt;
int tag[20];
//初始化栈
s->data = (int *)malloc(StackSize*sizeof(int));
s->top = -1;
//循环直到bt为空且栈s为空
while((p != NULL) || (s->top != -1))
{
/*循环直到p为空*/
while(p != NULL)
{
s->data[++(s->top)]=(int)p; //将p入栈
tag[s->top]=1; //将标志设为1
p=p->rchild; //继续遍历*bt的右子树
}
/*出栈并输出栈顶元素*/
while(s->top != -1 && tag[s->top]==2)
{
/*书上写的如下注释两行,坑爹啊,死循环啊*/
// p=(BiNode *)s->data[s->top--];
// printf("%c \n",p->data);
printf("%c \n",((BiNode *)s->data[s->top--])->data); //这是正确的
}
/*若栈非空,栈顶元素的标志改为2,准备遍历栈顶结点的左子树*/
if(s->top != -1)
{
tag[s->top] = 2;
p = ((BiNode *)s->data[s->top])->lchild; //准备遍历*bt的左子树
}
}
}
//层序遍历非递归实现
void LeverOrder(BiTree *bt,SeqQueue *Q)
{
BiNode *p;
//初始化一个队列
Q->data = (int *)malloc(QueueSize*sizeof(int));
if(Q->data == NULL)
return ;
//初始化空队列
Q->front = Q->rear = QueueSize - 1;
/*如果二叉树为NULL,算法结束*/
if((*bt) == NULL)
return ;
/*判断队列是否满*/
if(Q->front == (Q->rear + 1)%QueueSize)
{
printf("Queue is full!\n");
return ;
}
/*将树的根指针入队,由于队列data为int类型,因此将根指针强制类型转化为int型*/
Q->data[++Q->rear]=(int)*bt;
//printf("aaaaaa:%c\n",**bt);
while(Q->front != Q->rear) //队列不为NULL
{
//出队
//intf("aaaaaa:%c\n",Q->data[Q->front]);
p=(BiNode*)Q->data[++Q->front];
printf("%c\n",p->data);
if(p->lchild != NULL) //左孩子入队
Q->data[++Q->rear] = (int)p->lchild;
if(p->rchild != NULL) //右孩子入队
Q->data[++Q->rear] = (int)p->rchild;
}
}
void Release(BiTree *bt,SeqStack *s)
{
BiNode *p=*bt;
int tag[20];
/*初始化栈*/
s->data = (int *)malloc(StackSize*sizeof(int));
s->top = -1;
/*循环直到bt为空且栈s为空*/
while(p != NULL || s->top != -1)
{
while(p != NULL)
{
s->data[++(s->top)]=(int)p; //将*bt入栈
tag[s->top]=1;
p=p->lchild; //继续遍历*bt的左子树
}
while(s->top != -1 && tag[s->top]==2)
{
/*后序遍历出栈时,释放*/
free((BiNode *)s->data[s->top]);
//printf("%c \n",((BiNode *)s->data[s->top])->data); //这是正确的
s->top--;
}
if(s->top != -1)
{
tag[s->top]=2;
p = ((BiNode *)s->data[s->top])->rchild; //准备遍历*bt的右子树
}
}
}
main.c
#include "LinkBiTree.h"
int main(void)
{
BiTree B;
SeqStack s;
SeqQueue Q;
//初始化一棵扩展二叉树'AB D C @';
InitBiTree(&B,&s);
//前序遍历
printf("前序遍历:\n");
PreOrder(&B,&s);
//中序遍历
printf("中序遍历:\n");
InOrder(&B,&s);
//后序遍历
printf("后序遍历:\n");
PostOrder(&B,&s);
//层序遍历
printf("层序遍历:\n");
LeverOrder(&B,&Q);
//释放二叉树
// printf("aaaaaaaaa:%c\n",B->data);
Release(&B,&s);
// printf("SUCCESS:%c\n",(B->lchild)->data);
//PreOrder(&B,&s);
return 0;
}
6.5线索二叉树
6.6树,森林与二叉树的转换
6.6.1 树转换为二叉树
6.6.2森林转换为二叉树
6.6.3 二叉树转换为树
6.6.4 二叉树转换为森林
图 6-11-5
6.6.5 树与森林的遍历
(1)树的遍历
树的前序遍历 <==>二叉树的前序遍历
树的后序遍历<==>二叉树的中序遍历
(2)森林的遍历
6-11-5右侧三棵树的森林,后续遍历序列的结果是BCDAFEJHIG(即树的中序遍历)
6.7 二叉树的应用
哈夫曼树与哈夫曼编码
6.8 树的应用
八枚硬币问题