今天抽时间看了一下赫夫曼编码,感觉挺有意思。那就做个小总结。
赫夫曼树:带全路径长度WPL最小的二叉树称作赫夫曼树。具题看大话数据结构 200页 或 百度~
赫夫曼编码:按照叶子结点的权值从小到大来构造一棵赫夫曼树。 规定赫夫曼树的做分支代表0,右分支代表1,
则从根节点到叶子结点所经过的路径分支组成的序列称作赫夫曼编码。具题看大话数据结构 207页 或 百度~
我们来看下书上的例子:
假设六个字母的频率为 A 27 ,B 8,C 15,D 30,E 30,F 5,合起来正好是100%。
我们把频率低的看为结点权值小。那么我们就可以构造出一颗赫夫曼树。
可以用 结构体数组 和 优先队列 模拟建树。
具体实现看代码吧,已经在代码后面加上详细的注释了~~
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
int ans = 0, flag;
char chc[1000];
struct ak
{
int qz; //权值
char ch; //字母
int arr; //元素所在数组下标
bool operator < (const ak &a) const
{
return a.qz < qz; //优先队列重载操作符
}
}temp;
priority_queue<ak> pq;
struct Node
{
int dir[2]; //结点左孩子和右孩子
char cc; //字母
}s[100000];
int gao()
{
while(1)
{
int siz = pq.size();
int father = ans++;
s[father].cc ='$';
int cnt = 0;
for(int i = 0; i < 2 ; i++)
{
s[father].dir[i] = -1;
if(i < siz)
{
temp = pq.top();
pq.pop();
cnt += temp.qz;
if(temp.arr == -1)
{
s[ans].cc = temp.ch;
s[ans].dir[0] = s[ans].dir[1] = -1;
s[father].dir[i] = ans++;
}
else
{
s[father].dir[i] = temp.arr;
}
}
}
temp.qz = cnt;
temp.ch = '$';
temp.arr = father;
pq.push(temp);
if(pq.size() == 1)
{
return father; //如果队列元素只剩一个了,函数调用结束。
}
}
}
void init()
{
temp.arr = -1;
temp.qz = 27, temp.ch = 'A';
pq.push(temp);
temp.qz = 8, temp.ch = 'B';
pq.push(temp);
temp.qz = 15, temp.ch = 'C';
pq.push(temp);
temp.qz = 15, temp.ch = 'D';
pq.push(temp);
temp.qz = 30, temp.ch = 'E';
pq.push(temp);
temp.qz = 5, temp.ch = 'F';
pq.push(temp);
}
int dir(int x, int deep)
{
int k = (chc[deep] == '0' ? 0 : 1); //解码过程,如果当前数字是0那么往左走,否则往右
if(s[x].dir[k] != -1) //如果有子结点,那么继续递归
{
return dir(s[x].dir[k], deep+1);
}
else //如果没有子结点,递归结束
{
printf("%c", s[x].cc);
return deep-1;
}
}
int main()
{
init(); //把大话数据结构P206页的6个字母和频率放入优先队列中
int kk = gao(); //根据字母和频率构造赫夫曼树
gets(chc);
flag = 0;
int coun = 0;
for(; flag < strlen(chc); flag++, coun++)
{
flag = dir(kk, flag);
}
puts("");
return 0;
}