文章目录
第五章:树和二叉树
5.1:树和二叉树的定义
5.1.1:树的定义
5.1.2:树的基本术语
5.1.3:二叉树的定义
5.2:案例引入
5.3:数和二叉树的抽象数据类型定义
5.4:二叉树的性质和存储结构
5.4.1:二叉树的性质
5.4.1.1:两种特殊形式的二叉树
5.4.1.1.1:满二叉树
5.4.1.1.2:完全二叉树
5.4.2:二叉树的存储结构
5.4.2.1:顺序存储结构
//顺序存储结构
#define MAXTSIZE 100 //二叉数的最大结点
typedef TELemType SqBiTree[MAXTSIZE]; //0号单元存储跟结点
SqBiTree bt;
5.4.2.2:链式存储结构
//二叉数的链式存储结构
typedef struct BiTNode{
TElemType data; //结点数据域
struct BiTNode *lchild,*rchild; //左右孩子结点
}BiNode,*BiTree;
5.4.2.3:三叉链表
5.5:遍历二叉树和线索二叉数
5.5.1:遍历二叉数
5.5.1.1:遍历算法的实现
5.5.1.1.1:先序遍历
//二叉数先序遍历算法
Status PreOrderTraver(BiTree T)
{
if(T==NULL) return OK; //空二叉数
else
{
vist(T); //访问根结点 例如:输出根结点printf("%d\t",T->data);
PreOrderTraverse(T->lchlid); //递归遍历左子树
PreOrderTraverse(T->rchlid); //递归遍历右子树
}
}
5.5.1.1.2:中序遍历
//二叉数中序遍历算法
Status PreOrderTraver(BiTree T)
{
if(T==NULL) return OK; //空二叉数
else
{
PreOrderTraverse(T->lchlid); //递归遍历左子树
vist(T); //访问根结点 例如:输出根结点printf("%d\t",T->data);
PreOrderTraverse(T->rchlid); //递归遍历右子树
}
}
//算法5.1:中序遍历的递归算法
void InOrderTraverse(BiTree T)
{//中序遍历二叉数T的递归算法
if(T) //若二叉数非空
{
InOrderTraverse(T->lchild); //中序遍历左子树
count<<T->data; //访问根结点
InOrderTraverse(T->rchild); //中序遍历右子树
}
}
//算法5.1:中序遍历的递归算法
void InOrderTraverse(BiTree T)
{//中序遍历二叉数T的递归算法
if(T) //若二叉数非空
{
InOrderTraverse(T->lchild); //中序遍历左子树
count<<T->data; //访问根结点
InOrderTraverse(T->rchild); //中序遍历右子树
}
}
5.5.1.1.3:后序遍历
//二叉数后序遍历算法
Status PreOrderTraver(BiTree T)
{
if(T==NULL) return OK; //空二叉数
else
{
PreOrderTraverse(T->lchlid); //递归遍历左子树
PreOrderTraverse(T->rchlid); //递归遍历右子树
vist(T); //访问根结点 例如:输出根结点printf("%d\t",T->data);
}
}
5.5.1.1.4:中序非递归算法
//算法5.2:中序遍历的非递归算法
void InOrderTraverse(BiTree T)
{//中序遍历二叉数T的非递归算法
InitStack(S); p=T;
q=new BiTNode;
while(p||!StackEmpty(S))
{
if(p) //p非空
{
Push(S,p); //根指针进栈
p=p->lchild; //根指针进栈,遍历左子树
}
else //p为空
{
Pop(S,p); //退栈
count<<q->data; //访问根结点
p=q->rchild; //遍历右子树
}
} //while
}
5.5.1.1.5:层次遍历算法
//使用队形类型定义如下
typedef struct
{
BTNode data[MaxSize]; //存放对中元素
int front,rear; //队头和队尾指针
}SqQueue; //顺序循环队列类型
//二叉数层次遍历算法
void LevelOrder(BTNode *b)
{
BTNode *p; SqQueue *qu;
InitQueue(qu); //初始化队列
enQueue(qu,b); //根节点指针进入队列
while(!QueueEmpty(qu)) //队不为空,则循环
{
deQueue(qu,p); //出队结点p
printf("%c",p->data); //访问结点p
if(p->lchild!=NULL) enQueue(qu,p->lchild); //有左孩子时将其进队
if(p->rchild!=NULL) enQueue(qu,p->rchild); //有右孩子时将其进队
}
}
5.5.1.2:遍历算法的分析
5.5.1.3:二叉树的遍历算法应用
5.5.1.3.1:建立二叉树算法
//算法5.3:先序遍历的顺序建立二叉链表
void CreateBiTree(BiTree &T)
{//按先序次序输入二叉数中结点的值(一个字符),创建二叉链表表示的二叉树T
cin>>cn;
if(ch=='#') T=NULL; //递归结束
else //递归创建二叉树
{
T=new BiTNode; //生成根节点
T->data=ch; //根节点数据域置为ch
CreateBiTree(T->lchild); //递归创建左子树
CreateBiTree(T->rchild); //递归创建右子树
} //else
}
5.5.1.3.2:复制二叉树算法
//算法5.4 复制二叉树
void Copy(BiTree T,BiTree &NewT)
{//复制一颗和T完全相同的二叉树
if(T=NULL) //如果是空树,递归结束
{
NewT=NULL;
return;
}
else
{
NewT=new BiTNode;
NewT->data=T->data; //复制根结点
Copy(T->lchild,NewT->lchild); //递归复制左子树
Copy(T->rchild,NewT->rchild); //递归复制右子树
} //else
}
5.5.1.3.3:计算二叉树深度算法
//算法5.5 计算二叉树的深度
int Depth(BiTree T)
{//计算二叉树的深度
if(T=NULL) return 0; //如果是空树,深度为0,递归结束
else
{
m=Depth(T->lchild); //递归计算左子树深度记为m
n=Depth(T->rchild); //递归计算右子树慎独记为n
if(m>n) return(m+1); //二叉树的深度为m和n的较大者加1
else return(n+1);
}
}
5.5.1.3.4:计算二叉树叶子结点数算法
//算法5.6 统计二叉数中结点的个数
int NodeCount(BiTree T)
{//统计二叉数T中结点的个数
if(T=NULL) return 0; //如果是空树,则结点个数为0,递归结束
else return NoCount(T->lchild)+NoCount(T->rchild)+1;
//否则结点个数为左子树的结点个数+右子树的结点个数加1
}
5.5.2:线索二叉数
//二叉数的二叉线索存储表示
typedef struct BiThrNode
{
TEleTypem data;
struct BIThrNode *lchild,*rchild; //左右孩子指针
int LTag,RTag; //左右标志
}BithrNode,*BuThrTree;
5.5.2.1:先序二叉数
5.5.2.2:中序二叉数
//算法5.7:以结点p为根的子树中序线索化
void InThreading(BiThrTree p)
{//pre是全局变量,初始化时其右孩子指针为空,便于在数的最左点开始建线索
if(p)
{
InThreading(p->lchild); //左子树递归线索化
if(!p->lchild) //p的左孩子为空
{
p->LTag=1; //给p加上左线索
p->lchild=pre; //p的左孩子指针指向pre(前驱)
} //if
else p->LTag=0; //pre的右孩子为空
if(!pre->rchild)
{
pre->RTag=1; //给pre加上右线索
pre->rchild=p; //pre的右孩子指针指向p(后驱)
} //if
else pre->RTag=0; //保持pre指向p的前驱
pre=p; //右子树递归线索化
InThreading(p->rchild);
}
}
//算法5.8 带头结点的二叉树中序线索化
void InOrderTreading(BiThrTree &Thrt,BiThrTree T)
{//中序遍历二叉数T,并将其中序线索化,Thrt指向头结点
Thrr=new BiTrNode; //建立头结点
Thrt->LTag=0; //头结点有左孩子,若树非空,则其左孩子为树根
Thrt->RTag=1; //头结点的右孩子指针为右线索
Thrt->rchild=Thrt; //初始化时右指针指向自己
if(!T) Thrt->lchild=Thrt; //若树为空,则左指针也指向自己
else
{
Thrt->lchild=T; pre=Thrt; //头结点的左子树指向根,pre初值指向头结点
InTreading(T); /*用算法5.7(以结点p为根的子树中序线索化),
对以T为根的二叉树进行中序线索化*//
pre->rchild=Thrt; //算法5.7结束后,pre为最右结点,pre的右线索指向头结点
pre->RTag=1;
Thrt->rchild=pre; //头结点的右线索指向pre
}
}
//算法5.9 遍历中序线索二叉数
void InOrderTraverse_Thr(BiThrTree T)
{//T指向头结点,头结点的左链lchild指向根结点
//中序遍历二叉树线索树T的非递归算法,对每个数据元素直接输出
p=T->lchild; //p指向根结点
while(p!=T) //空树或遍历结束时,p==T
{
while(p->LTag==0) p=p->lchild; //沿左孩子向下
count<<p->data; //访问其左子树为空的结点
while(p->RTag==1&&p->rchild!=T)
{
p=p->rchild; count<<p->data; //沿右线索访问后继结点
}
p=p->rchild; //转向p的右子树
}
}
5.5.2.3:后序二叉树
5.5.2.4:练习
5.6:树和森林
5.6.1:树的存储结构
5.6.1.1:双亲表示法
//C语言类型描述:
typedef struct PTNode
{
TElemType data;
int parent; //双亲位置域
}PTNode;
//树结构
#define MAX_TREE_SIZE 100;
typedef struct
{
PTNode nodes[MAX_TREE_SIZE];
int r,n;//跟结点的位置和结点个数
}PTree;
5.6.1.2:孩子链表
//孩子结点结构
typedef struct CTNode
{
int child;
struct CTNode *next;
}*Child;
//双亲结点结构
typedef struct
{
TElemType data;
ChildPtr firstchild; //孩子链表指针
}CTBox;
//树结构
typedef struct
{
CTBox nodes[MAX_TREE_SIZE];
int n,r;//结点数和跟结点的位置
}CTree;
5.6.1.3:孩子兄弟表示法
//孩子兄弟法
typedef struct CSNode
{
ElemType data;
struct SCNode *fristchild,*nextsibling;
}CSNode,*CSTree;
5.6.2:森林和二叉树的转换
5.6.2.1:树和二叉树的转换
5.6.2.2:森林和二叉树的转换
5.6.3:树和森林的遍历
5.7:哈夫曼树及其应用
5.7.1:哈夫曼树的基本概念
5.7.2:哈夫曼树的构造算法
//算法 5.10:构造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT,int n)
{//构造哈夫曼树
if(n<=1) return;
m=2*n-1;
HT=new HTNode[m+1]; //0好单元未用,所以需要动态分配m+1个单元,HT[m]表示根结点
for(i=1;i<m;i++) //将1-m号单元中的双亲,左孩子,右孩子的下标都初始化为0
{
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(i=1;i<=n;i++) //输入前n个单元中叶子结点的权值
cin>>HT[i].weight;
/*初始化工作结束,下面创建哈夫曼树*/
for(i=n+1;i<=m;i++)
{//通过n-次的选择,删除,合并来创建哈夫曼树
Select(HT,i-1;s1,s2);
//在HT[K](0<=k<=k-1)中选择两个其双亲域为0其权值最小的结点,并返回它们在HT中的序号s1和s2
HT[s1].parent=i;HT[s2].parent=i;
//得到新结点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为i
HT[i].lchild=s1;HT[i].rchild=s2; //s1s2分别作为i的左右孩子
HT[i].weight=HT[s1].weight+HT[s2].weight; //i的权值为左右孩子权值之和
}
}
5.7.3:哈夫曼编码
5.7.3.1:哈夫曼编码思想
5.7.3.2:哈夫曼编码的算法实现
//算法 5.11根据哈夫曼树求哈夫曼编码
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
HC=new char*[n+];
cd=new char[n];
cd[n-1]='\0';
for(i=14;i<=n;i++)
{
start=n-1;
c=i;f=HT[i].parent;
while(!f=0)
{
--start;
if(HT[f].lchild==c) cd[start]='0';
else cd[start]='1';
c=f;f=HT[f],parent;
}
HC[i]=new char[n-start];
strcpy(HC[i],&cd[start]);
}
delete cd;
}
5.7.3.3:文件的编码和译码