昨天本来就把这篇文章发出来了,但是程序有一点小的问题,而且没有解码步骤,几天全部补上。
霍夫曼编码是Huffman在MIT的博士毕业论文中提出的一种编码方法。因为它的简单实用,所以虽然已经过去了很多很多年,但这种方法依然经久不衰。说来惭愧,虽说是通信专业科班出身,但是信息论的内容已经忘得差不多了,只记住了如何编码,而这种编码背后所蕴含的复杂的数学推导(否则也不能作为博士论文发表啊),已经几乎没有印象了。
通俗的说,就是假如我们知道我们要发的消息一定是由a,b,c,d,e,f,g,h8个字母组成的。而且每个字符出现的概率是确定的。比如a出现概率有60%,b有20%等等,它的基本思想是,对于出现频率高的字符,用较少的比特表示,而出现频率低的字符,则用较长的比特表示。这样与直接从000到111的平局分配相比,当字符很多的时候,可以减少总的比特数。
它的编码过程如下:
假设N个字符构成了集合h
1.从h中选择2个概率最小的字符x,y,它们的权值分别为wx,wy。
2.用x,y构造二叉树X。X的左右节点为x,y,这个节点的权值为wx+wy
3.将新产生的二叉树X加入到集合h中,同时将x,y删去
4.不断重复1~3,直到h中只剩下一个元素。
5.沿着产生的树,一边为0,另一边为1,直到叶子节点,则每个叶子节点经过的0、1路径就是该叶子节点对应字符的编码。
让我们先看看定义的数据结构:
//26个英文字母
#define SIZE 26
typedef struct Honde
{
char val;
double p;//概率值
int parent;
int LeftChild;
int RightChild;
}Hnode,*pHnode;
//数组用来承载集合h
Hnode H[400];
程序中有一点小的技巧,就是树的父亲、孩子指针不再是指向另一棵树,而是存放这棵树在树类型的数组H中的下标。
让我们先看看主函数:
#include "huffman.h"
int main()
{
if(!initData())
return -1;
int hsize = HuffmanCode(SIZE);
decode(hsize);
return 0;
}
可以看出,整个程序分为3个步骤:初始化数据,编码,以及解码。
初始化数据的任务,就是读取一份文件,对立面的字数进行统计,并求出对应的概率。这里为了简单,只统计了小写字母。
//读入文本,并统计每个字符的概率
bool initData()
{
FILE *fp;
//注意:如果使用\表示下一级目录,则会当做转义字符
fp = fopen("D:\\MyPrograms\\ds\\mytext.txt","r");
i