概述
哈夫曼编码可以有效的压缩数据,通常可以节省20~90%的空间,具体压缩率依赖于数据的特性。我们将待压缩数据看作字符序列,根据每个字符出现的频率(也可以是字符对应的权重),通过哈夫曼贪心算法构造出字符的最优二进制表示。
这里我们只考虑前缀码,即没有任何码字是其他码字的前缀。前缀码可以保证达到最优的数据压缩率,且不会丧失一般性。但这里不做它做出证明。接下来我们介绍一下Huffman算法
算法设计思路
哈夫曼设计了一个贪心算法来构造最优前缀码,被称为Huffman code。它的正确性证明也依赖于贪心选择性质和最优子结构。
首先我们先设计一下存储结构,采用顺序表存储,开辟两个数组,一个用于存储Huffman树的结点,另一个用于存储Huffman树各个数据对应的前缀码,代码如下:
typedef struct huffman_Node // 结点
{
char data;
int weight;
int parent, lchild, rchild;
} Node;
typedef struct huffman_Code // 编码
{
char data;
string code;
} Code;
bool flag[N] = {
false};//标志结点是否已经被加入树中
Node huff_tree[N];//Huffman树
Code huff_code[N];//前缀码数组
int n;//数据个数
接着我们根据输入的数据进行Huffman树的建树过程,这里我们采用顺序表结构进行存储,假设C是一个n个字符的集合,每个字符c∈C,c.weight对应它的出现频率(或者说是它的权重),然后先将n个字符存入Huffman树的数组中,其对应下标为0-n-1。接着,采用自底向上的构造出对应的最优编码的二叉树。从C中元素出发,进行|C|-1次合并操作创建出最终的二叉树。算法主要通过找到当前集合中出现频率(权重)最小的两个对象,将其合并,当合并两个对象时,得到的新对象的频率(权重)为原来两个对象的频率(权重)之和。
代码如下:
int find_min(int m)
/**
* @description: 该函数意在找到森林中未被找到过的
* 且权值为最小的那颗树,采用顺序查找来搜索,最终
* 返回它的下标
* @param {*int m}
* @return {*return id}
*/
{
int mi = INT_MAX, id = -1;
for (int i = 0; i < m; ++i)
{
if (!flag[i] && huff_tree[i].weight < mi)
{
mi = huff_tree[i].weight;
id = i;
}
}
flag[id] = true;
return id;
}
void build()
/**
* @description: 该函数为建立huffman树函数
* 每次从森林中选取权值最小的两颗树,将其合并
* 为一颗新树并加入森林中,然后将这两颗树删除
* 显然每做一次操作减少一颗树,当进行了n-1次
* 就可以得到最终形成的huffm树
*/
{
int i, cnt = 0;
pr("请输入字符个数:\n");
input(n);
pr("请输入字符: \n");
for (i = 0; i < n; ++i)
{
cin >> huff_tree[i].data;
huff_tree[i].weight = (int)huff_tree[i].data;
huff_tree[i].lchild = huff_tree[i].rchild = -1;
}
// 左0右1 左小右大
i = n;
while (1)
{
if (cnt == n - 1)
{
int ii = find_min(i);
huff_tree[ii].parent = -1;
break;
}
int i1 = find_min(i);
int i2 = find_min(i)