文章目录
- 1.哈夫曼树
- 2.如何构造最优树
- 3.哈夫曼编码
1.哈夫曼树
- 结点的路径长度:从根节点到该节点的路径上分支的数目
- 树的路径长度:树中每个节点的路径长度之和
- 树的带权路径长度:树中所有叶子节点的权值和路径长度的乘积的总和
假设树中有m个叶子节点,每个叶子节点的权值为Wi,从根到叶子节点的路径长度为Li
称树的带权路径长度最短的一类树为“最优树“,即:哈夫曼树 - 有4个结点,权值分别为7, 5, 2, 4,构造有4个叶子结点的二叉树
2.如何构造最优树
- 例如: 已知权值 W={ 5, 6, 2, 9, 7 }
- eg:在解决某些判定问题时, 利用哈夫曼树可以得到最佳的判定算法, 例如, 要编制一个将百分制转换为五级分制的程序。
3.哈夫曼编码
- 如:需将文字“ABACCDA” 转换成电文。
文字中有四种字符,用2位二进制便可分辨。
编码方式1:电文长度为14位 - 编码方式2:无法译码。
00是A,还是B? - 哈夫曼编码:既能译码,编码的长度还短,电文长度总共13位
- 哈夫曼编码具体过程
- 哈夫曼编码的算法
在构造哈夫曼树时, 可以设置一个结构数组HuffNode保存哈夫曼树中各结点的信息, 根据二叉树的性质可知, 具有n个叶子结点的哈夫曼树共有2n- 1个结点, 所以数组HuffNode的大小设置为2n- 1, 数组元素的结构形式如下:
#define MAXBIT 10//哈夫曼编码的最大长度
#define MAXVALUE 1000//哈夫曼树叶子节点的权值的最大值
typedef struct HNode//定义节点结构
{
int weight;
int parent,child,rchild;//双亲域,左孩子域,右孩子域
}HNode;
typedef struct HCode//哈夫曼编码结构
{
int bit[MAXBIT];
int start;//哈夫曼编码的开始位置
}HCode;
//构造哈夫曼树,以及求出n个字符对应的哈夫曼编码
void HuffmanCoding(HNode *HT, HCode *C, int *w, int n)
{
//w存放n个字符的权值,构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC
if (n<=1)
return;
m=2*n-1;//包含:n个叶子节点和n-1个非叶子节点
//哈夫曼树的构造
HT=(HNode *)malloc(m)*sizeof(HNode);
for(p=HT,i=1;i<n;++i,++p,++w)//初始化叶子节点信息
{
(*p)->weight=*w;//叶子节点对应的权值
(*p)->lchild=-1;//叶子节点没有左孩子和右孩子
(*p)->rchild=-1;
(*p)->parent=-1;//初始情况下,叶子节点的双亲未知
}
for (;i<m;++i,++p)//初始化分支节点信息
{
(*p)->weight=0;
(*p)->lchild=-1;
(*p)-rchild=-1;
(*p)->parent=-1;
}
//寻找根节点权值最小和次小的两棵子树
for (i=n;i<m;++i)
{
m1=m2=MAXVALUE;//用m1和m2才存放具有最小和次小根节点权值的两颗子树,权值初始化为最大值
x1=x2=0;//存放根节点在这个结构数组中的下标地址
for (j=0;j<i;++j)
{
if (HT[j].parent==-1 && HT[j].weight<m1)//找到权值最小的两颗子树
{
m2=m1;
x2=x1;//根节点对应的位置用x1和x2来存放,对应两个根节点x1和x2
m1=HT[j].weight;//节点对应的权值用m1和m2来存放
x1=j;
}
else if(HT[j].parent==-1 && HT[j].weight<m2)
{
m2=HT[j].weight;
x2=j;
}
//合并成一颗子树
//i是x1和x2的双亲节点
HT[x1].parent=i;
HT[x2].parent=i;
HT[i].lchild=x1;
HT[i].rchild=x2;
HT[i].weight=m1+m2;
}
}
//字符编码,从叶子节点找到到根节点的一条路径,左分支=0,右分支=1,从根节点到叶子节点的0 1串构成了哈夫曼编码
HC=(HCode *)malloc(n)*sizeof(HCNode);
for(i=0;i<n;i++)//对n个叶子节点
{
start=n-1;//最后一个非叶子节点的位置
for(c=i,f=HT[i].paent;f!=-1;c=f,f=HT[f].parent)
{
if(HT[f].lchild=c)
HC[i].bit[start--]=0;//若发现为左分支,就将响应的哈夫曼编码置为0
else
HC[i].bit[start--]=1;//若发现为右分支,就将响应的哈夫曼编码置为1
HC[i].start=start+1;//记录编码的起始位置
}
}
}