有点儿兴奋,有点儿紧张,这是我人生发的第一篇正式的blog,在进入正文之前,请容许我说一点儿序言,之所以发这一blog文章,一来是想记录下自己一天做了些什么,学了些什么,所谓:好记性不如烂笔头,以前没有体会到这一点儿的好,在经过一个项目的总结之后,看到同事在做一个项目的总结文档和自己的一比,才知道,原来实时记录自己所学,所想的重要性,成功是一点儿一点儿积累起来的;二来,也是便于自己经常的复习所学的东西,不致于要用的时候,到处找,像无头苍蝇一般;
好了,让我们进入正题吧,今天所学的是用代码实现“赫夫曼编码”。当然,什么是赫夫曼编码呢?在此,我就不多说了,因为本人也不是很了解,只知道“赫夫曼编码”又称“前缀编码”,在《数据结构(C语言)版——严蔚敏感编著》中第146页有些许的介绍。有兴趣的朋友可以下去多了解学习一下,本人只注重代码的实现。谢谢!
让我先来看看生成“赫夫曼编码”所需的数据结构:
typedef struct_huffman_node
{
unsigned int weight;
unsigned int parent,lchild,rchild;
_huffman_node(const int w,const int p,const int l ,const int r)
{
weight=w;parent=p;lchild=l;rchild=r;
}
bool operator<(const _huffman_node t)
{
return weight<t.weight;
}
}HTNode,*HuffmanTree;
typedef char ** HuffmanCode;//动态分配数组存储赫夫曼编码表
注:之所以把父指针、左子树、右子树定义为unsigned int是因为我采用的是数据存储结构是数组,故,parent、lchild、rchild存放的是相应的下标值,无需用指针来存储。当然,网上也有很多利用其二叉树来实现的方法,由于自己还没有达到那个高度,所以只好先用数组来实现了,慢慢来,慢慢提高,慢慢的接近大神。
在这个结构体中,还定义了一个结构体的构造函数,与“<”运算符的函数。这样便于对结构体赋值及比较。稍后,为大家带来详细的讲解。
其次,来看实现编码的函数。
void HuffmanCoding( HuffmanTree &HT,HuffmanCode& HC,int *w,int n )
{
if (w==NULL || n<=1)
{
return;
}
if (HT!=NULL || HC !=NULL)
{
throw INVALID_PARAMETER;
return;
}
int m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//从下标开始计数
if (HT == NULL)
{
return;//是否还有什么工作没有做
}
HuffmanTree p = HT;
*p=HTNode(0,0,0,0);
p++;
for (int i =1; i<=n; ++i,++p,++w)
{
*p=HTNode(*w,0,0,0);//利用结点的权值,初始化huffman树的前n个结点。这就是用了结构体的构造函数好处,可以少写很多的赋值代码。
}
for(int i =n+1;i<=m;i++,++p)
{
*p=HTNode(0,0,0,0);//初始化huffman树中的双亲节点。
}
unsigned int s1=0,s2=0;
for(int i=n+1;i<=m;++i)
{
//建立赫夫曼树
SelectSubtree(HT,i-1,s1,s2);//可能有问题,如果函数中有异常的运行情况怎么办。
HT[s1].parent=i;HT[s2].parent=i;
HT[i].lchild=s1;HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
//
/*
*从叶子到根逆向求每个字符的赫夫曼编码
*/
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
char * cd =(char*)malloc(n*sizeof(char));
if (HC==NULL || cd ==NULL)
{
return ;//是否还有什么别的工作没有做。
}
cd[n-1]='\0';
int start=0;
int c =0 ,f =0,len=0;
for (int i = 1;i<=n;++i)
{
//逐个字符求赫夫曼编码
start = n-1;
for (c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)
{
if (HT[f].lchild==c)
{
cd[--start]='0';
}
else
{
cd[--start]='1';
}
}
len = n-start;
HC[i] = (char *)malloc((len+1)*sizeof(char));//为第i个字符编码分配空间
memset(HC[i],0,len+1);
strcpy_s(HC[i],len,&cd[start]);
}
free(cd);
}
注:1、要注意函数中,多处用了malloc动态的开辟内存空间,要及时的对新开辟的内存空间初始化。
2、要注意对开辟空间数据的操作,本人在对此指针操作的时候,多次造成指针越界,调试非常的痛苦,请朋友们一定要注意啦。如:strcpy_s函数,开始由于没有对cd开辟的空间做初始化为0的操作,造成了,函数多次弹出“buffer too small”的bug.
第三,HuffmanCoding函数中调用了一个SelectSubtree(...)函数,当然函数的定义如下,
void SelectSubtree( const HuffmanTree &HT,int limit,unsigned int & s1,unsigned int& s2 )
{
if (HT == NULL || limit<=0)
{
throw INVALID_PARAMETER;//参数异常
}
unsigned int min_1 = 0,min_2 =0;
for (int i = 1;i<=limit;i++)
{
if (HT[i].parent!=0)
{
continue;
}
else if (min_1==0)
{
min_1=i;
}
else if (min_2 == 0)
{
if (HT[i]<HT[min_1])
{//这里就利用了结构体中”<”运算符的作用,是不是看得很直观啊!
min_2=min_1;
min_1=i;
}
else
{
min_2=i;
}
}
else if (HT[i]<HT[min_1])
{
min_2=min_1;
min_1=i;
}
else if (HT[i]<HT[min_2])
{
min_2 = i;
}
}
s1=min_1;
s2=min_2;
}
好了,到此,今天的blog已接近尾声了,希望能帮助到难看的游客朋友,如果,你发现了其中的不足,请您提出您的的宝贵建议,让我们共同提高,共同进步,本人的QQ:1272725,当然,要是你还有什么不明白的地方,也可以加本人的QQ联系,或者留言,一起讨论。谢谢……