补充知识点:线索二叉树
一、树、二叉树和森林互相转换
1.1 树转二叉树
树中每个结点最多只有一个最左边的孩子(长子)和一个右邻的兄弟。按照这种关系很自然地就能将树转换成相应的二叉树:1.在所有兄弟结点之间加一连线2.对每个结点,除了保留与其长子的连线外,去掉该结点与其它孩子的连线。
也就是说,在转换成二叉树之后,除了根结点的每一个结点的右孩子时该结点的兄弟,左孩子是该结点的长子。
如下图所示:
1.2 二叉树转树
是树转换为二叉树的逆过程。
1.加线。若某结点X的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点…,都作为结点X的孩子。将结点X与这些右孩子结点用线连接起来。
2.去线。删除原二叉树中所有结点与其右孩子结点的连线。
如下图所示:
1.3 森林转二叉树
具体的方法是:
1.将森林中的每棵树变为二叉树;
2.因为转换所得的二叉树的根结点的右子树均为空,故可将各二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树。
也就是说,根结点的右孩子是一棵树,左孩子是长子,长子的右孩子是兄弟,长子的左孩子是孩子。
如下图所示:
1.4 森林转二叉树
具体的方法是:
1.将森林中的每棵树变为二叉树;
2.因为转换所得的二叉树的根结点的右子树均为空,故可将各二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树。
如下图所示:
二、 哈夫曼树
哈夫曼树是一种带权(W)路径(P)长度(L)最短的二叉树,也称为最优二叉树。
2.1 创建哈夫曼树
1,将所有左,右子树都为空的作为根节点。
2,在森林中选出两棵根节点的权值最小的树作为一棵新树的左,右子树,且置新树的附加根节点的权值为其左,右子树上根节点的权值之和。注意,左子树的权值应小于右子树的权值。
3,从森林中删除这两棵树,同时把新树加入到森林中。
4,重复2,3步骤,直到森林中只有一棵树为止,此树便是哈夫曼树。
如下图所示:
2.3 哈夫曼编码
利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。树中从根到每个叶子节点都有一条路径,对路径上的各分支约定指向左子树的分支表示”0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为各个叶子节点对应的字符编码,即是哈夫曼编码。
上图A,B,C,D的哈夫曼编码为:111,10,110,0
注意:设计电文总长度最短的二进制前缀编码,就是以n个字符出现的频率
作为权构造一颗哈夫曼树,由哈夫曼树求得的编码就是哈夫曼编码。
参考博客(里面有C#代码实现):数据结构和算法系列16 哈夫曼树
三、二叉树遍历上机任务
#include <stdio.h>
#include <stdlib.h>
typedef struct BiTNode{
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BinTree;
int n0,n1,n2; //用全局变量分别统计叶子结点、单孩子结点、双孩子结点个数
void CreateBiTree(BinTree *T); //构造二叉链表
void InOrder(BinTree T); //中序遍历
void PostOrder(BinTree T); //后序遍历
int Leafnum(BinTree p); //统计叶子结点数目
int Onechildnum(BinTree p); //统计单孩子结点数目
int Twochildnum(BinTree p); //统计双孩子结点数目
void main() //主函数
{
BinTree T;
char ch;
while(1)
{
system("cls");
printf("\n\t二叉树的基本操作:\n");
printf("\n\tA -----二叉树建立-------");
printf("\n\tB -----中序遍历---------");
printf("\n\tc -----后序遍历---------");
printf("\n\td -----求叶子节点个数---------");
printf("\n\te -----求单孩子节点个数---------");
printf("\n\tf -----求双孩子节点个数---------");
printf("\n\n\t请选择:");
scanf("\n%c",&ch);
switch(ch)
{
case 'a':
case 'A':printf("\t请按先序输入二叉树存储的结点序列:");
CreateBiTree(&T);
break;
case 'b':
case 'B':printf("\t该二叉树的中序遍历序列为:"); //调用中序遍历函数
InOrder(T);
break;
case 'C':
case 'c':printf("\t该二叉树的后序遍历序列为:");
PostOrder(T);
break;
case 'd':
case 'D':printf("\t该二叉树的叶子节点数为:%d",Leafnum(T));
break;
case 'e':
case 'E':printf("\t该二叉树的单孩子节点数为:%d",Onechildnum(T));
break;
case 'f':
case 'F':printf("\t该二叉树的双孩子节点数为:%d",Twochildnum(T));
break;
default:printf("\n\t输入错误,重新选择");
}
printf("\n\t");
system("pause");
}
}
void CreateBiTree(BinTree *T) //建立二叉链表
{
char c;
scanf("\n%c",&c);
if(c=='0')
*T=NULL;
else
{
*T=new BiTNode;
(*T)->data =c;
CreateBiTree(&(*T)->lchild);//构造左子树
CreateBiTree(&(*T)->rchild);//构造右子树
}
}
void InOrder(BinTree T) //中序遍历
{
if(T== NULL)
return;
PostOrder(T->lchild);
printf("%c",T->data);
PostOrder(T->rchild);
}
void PostOrder(BinTree T) //后序遍历
{
if(T== NULL)
return;
PostOrder(T->lchild);
PostOrder(T->rchild);
printf("%c",T->data);
}
int Leafnum(BinTree p) //计算二叉树叶子结点数目
{
if(p==NULL)
return 0;
if(p->lchild==NULL&&p->rchild==NULL)
return 1;
return (Leafnum(p->lchild)+Leafnum(p->rchild));
}
int Onechildnum(BinTree p) //计算单孩子结点数目
{
if(p==NULL)
return 0;
if((p->lchild !=NULL && p->rchild==NULL) || (p->lchild ==NULL && p->rchild!=NULL))
return 1;
return (Onechildnum(p->lchild)+Onechildnum(p->rchild));
}
int Twochildnum(BinTree p) //计算双孩子结点数目
{
if(p==NULL)
return 0;
if(p->lchild !=NULL && p->rchild!=NULL )
return 1;
return (Twochildnum(p->lchild)+Twochildnum(p->rchild));
}