哈夫曼树及编译码
哈夫曼树,又称二叉树,是一类带权路径长度最短的树。所谓路径长度,就是节点到树根之间的路径长度与节点权值的乘积。
哈夫曼本人曾在MIT的信息论研究生班学习。Robert Fano教授让学生们自己决定是参加期未考试还是做一个大作业。而哈夫曼选择了后者,原因很简单,因为解决一大作业可能比期未考试更容易通过。Robert Fano教授也是信息论的先驱,学过信息论的都知道有Fano不等式,Shannon-Fano编码。当时这个大作业,Fano也解决不了,哈夫曼并不知道,于是自己尝试,最终产生了哈夫曼编码,其性能比Shannon-Fano编码更好。这个故事说明,大师级人物未能解决的问题,我们不一定解决不了,因为我们的思想比较开阔,能从不同的角度看问题。还有就是turbo码的产生也印证了这个道理。但是任何成功都离不开坚持不懈的努力。这段小故事就当你我共勉。
哈夫曼树
哈夫曼树的构造由下图可清楚明了:(总的来说就是每次将两个最小的节点合并)
用上述算法来对图12.16中的叶子结点集合构造哈夫曼树的初始状态如图12.18(a)所示,第一次合并状态如图12.18(b)所示,结果状态如图12.18(c)所示。在算法中,每次合并时都是将具有较小权值的结点置为合并后结点的左孩子,而具有较大权值的结点置为合并后结点的右孩子。
具体实现如下:
/**********************************************************\
函数功能:构造哈夫曼树
输入: 头结点、权重、元素值
输出: 无
\**********************************************************/
void HuffmanTree(huffmantree *tree,double *weight,int *data)
{
int i,j;
for(i=0;i<m;i++)//初始化
{
tree[i].parent=0;
tree[i].lchild=0;
tree[i].rchild=0;
tree[i].weight=0.0;
tree[i].data=0;
}
for(i=0;i<n;i++)
{
tree[i].weight=weight[i];//给每个节点赋权值和内容
tree[i].data=data[i];
}
for(i=n;i<m;i++)
{
int p1=0;
int p2=0;
float small1,small2;
small1=small2=10000;//初始化为一个很大的值
for(j=0;j<=i-1;j++)//找出最小权重的两个节点
{
if(tree[j].parent==0)
{
if(tree[j].weight<small1)
{
small2=small1;
small1=tree[j].weight;
p2=p1;
p1=j;
}
else if(tree[j].weight<small2)
{
small2=tree[j].weight;
p2=j;
}
}
}
tree[p1].parent=i;
tree[p2].parent=i;
tree[i].lchild=p1;
tree[i].rchild=p2;
tree[i].weight=tree[p1].weight+tree[p2].weight;//将两个节点合并为一个节点
}
}
/**************************************************\
函数功能:进行哈夫曼编码
输入: 用于存储编码的数组code、哈夫曼树
输出: 无
\**************************************************/
void HuffmanCode(codetype *code,huffmantree *tree)
{
int i,c,p;
codetype cd;//缓冲变量
for(i=0;i<n;i++)
{
cd.start=n;//从叶子节点开始回溯
c=i;
p=tree[c].parent;
cd.data=tree[c].data;
while(p!=0)
{
cd.start--;
if(tree[p].lchild==c)//左节点则编为0,右节点则编为1
cd.bits[cd.start]=0;
else
cd.bits[cd.start]=1;
c=p;
p=tree[c].parent;
}
code[i]=cd;
code[i].start=cd.start;
}
}
哈夫曼译码:
/***************************************************\
函数功能:哈夫曼译码
输入: 存储编码的数组、哈夫曼树
输出: 无
\***************************************************/
void HuffmanDecode(codetype *code,huffmantree *tree)
{
int i=m-1;
printf("\n译码结果为:\n");
for(int j=0;j<n;j++)
{
for(int k=code[j].start;k<n;k++)//循环n次,对n个码进行译码
{
if(code[j].bits[k]==0)
i=tree[i].lchild;
else
i=tree[i].rchild;
if(tree[i].lchild==0)
{
printf("%d ",code[i].data);
i=m-1;
}
}
}
}
完整实例如下:假设有6个节点,权值分别为0.4、0.3、0.1、0.1、0.08、0.02,元素值分别为2、1、3、4、6、5.则哈夫曼树的构造过程和编码如下:
#include<stdio.h>
#define n 6 //叶子数目
#define m (2*n-1)//节点总数
#define maxsize 10
typedef int datatype;
typedef struct
{
double weight;
datatype data;
int lchild,rchild,parent;
}huffmantree;
typedef struct
{
int bits[n];
int start;
int data;
}codetype;
void HuffmanTree(huffmantree *tree,double *weight,int *data);
void HuffmanCode(codetype *code,huffmantree *tree);
void HuffmanDecode(codetype *code,huffmantree *tree);
void main()
{
double weight[]={0.4,0.3,0.1,0.1,0.02,0.08};
int data[]={2,1,3,4,5,6};
huffmantree head[m];
codetype code[n];
HuffmanTree(head,weight,data);
HuffmanCode(code,head);
for(int i=0;i<m;i++)
printf("%d ",head[i].parent);//对应的父节点
printf("\n编码结果为:\n");
for(int i=0;i<n;i++)
{
for(int j=code[i].start;j<n;j++)
printf("%d ",code[i].bits[j]);
printf("\n");
}
HuffmanDecode(code,head);
}
/**********************************************************\
函数功能:构造哈夫曼树
输入: 头结点、权重、元素值
输出: 无
\**********************************************************/
void HuffmanTree(huffmantree *tree,double *weight,int *data)
{
int i,j;
for(i=0;i<m;i++)//初始化
{
tree[i].parent=0;
tree[i].lchild=0;
tree[i].rchild=0;
tree[i].weight=0.0;
tree[i].data=0;
}
for(i=0;i<n;i++)
{
tree[i].weight=weight[i];//给每个节点赋权值和内容
tree[i].data=data[i];
}
for(i=n;i<m;i++)
{
int p1=0;
int p2=0;
float small1,small2;
small1=small2=10000;//初始化为一个很大的值
for(j=0;j<=i-1;j++)//找出最小权重的两个节点
{
if(tree[j].parent==0)
{
if(tree[j].weight<small1)
{
small2=small1;
small1=tree[j].weight;
p2=p1;
p1=j;
}
else if(tree[j].weight<small2)
{
small2=tree[j].weight;
p2=j;
}
}
}
tree[p1].parent=i;
tree[p2].parent=i;
tree[i].lchild=p1;
tree[i].rchild=p2;
tree[i].weight=tree[p1].weight+tree[p2].weight;//将两个节点合并为一个节点
}
}
/**************************************************\
函数功能:进行哈夫曼编码
输入: 用于存储编码的数组code、哈夫曼树
输出: 无
\**************************************************/
void HuffmanCode(codetype *code,huffmantree *tree)
{
int i,c,p;
codetype cd;//缓冲变量
for(i=0;i<n;i++)
{
cd.start=n;//从叶子节点开始回溯
c=i;
p=tree[c].parent;
cd.data=tree[c].data;
while(p!=0)
{
cd.start--;
if(tree[p].lchild==c)//左节点则编为0,右节点则编为1
cd.bits[cd.start]=0;
else
cd.bits[cd.start]=1;
c=p;
p=tree[c].parent;
}
code[i]=cd;
code[i].start=cd.start;
}
}
/***************************************************\
函数功能:哈夫曼译码
输入: 存储编码的数组、哈夫曼树
输出: 无
\***************************************************/
void HuffmanDecode(codetype *code,huffmantree *tree)
{
int i=m-1;
printf("\n译码结果为:\n");
for(int j=0;j<n;j++)
{
for(int k=code[j].start;k<n;k++)//循环n次,对n个码进行译码
{
if(code[j].bits[k]==0)
i=tree[i].lchild;
else
i=tree[i].rchild;
if(tree[i].lchild==0)
{
printf("%d ",code[i].data);
i=m-1;
}
}
}
}
注:如果程序出错,可能是使用的开发平台版本不同,请点击如下链接: 解释说明
原文:http://blog.csdn.net/tengweitw/article/details/9838343
作者:nineheadedbird