Huffman树的存储结构:结构数组。
Huffman树造成的关键:如何在一堆带权节点中每次选取两个权值最小的节点。
1.哈夫曼树
哈夫曼树又称最优二叉树。它是 n 个带权叶子结点构成的全部二叉树中,带权路径长度 WPL 最小的二叉树。html
以下图为一哈夫曼树示意图。node
二、构造哈夫曼树
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w一、w二、…、wn,则哈夫曼树的构造规则为:ios
(1) 将w一、w二、…,wn当作是有n 棵树的森林(每棵树仅有一个结点);数组
(2) 在森林中选出两个根结点的权值最小的树合并,做为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;编码
(3)从森林中删除选取的两棵树,并将新树加入森林;spa
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
.net
如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤以下:设计
图片来源:点击打开连接指针
下面采用暴力求解的方法构建HuffmanTree和HuffmanCode
完整代码以下:
/* 根据Huffman编码的原理,编写一个程序,在用户输入节点权重的基础上创建他的Huffman编码 */
//解题思路:先构造一棵Huffman树,由此获得的二进制前缀便为Huffman
//因为Huffman树没有度为1的节点,则一棵有着n个节点的Huffman树共有2n-1个节点,设计一个结构数组,存储2n-1个节点的值
//包括权重,父节点、左节点、右节点等
#include
using namespace std;
#define MAX 21
typedef struct
{
char data; //节点值
int weight; //权重
int parent; //父节点下标位置
int left; //左节点
int right; //右节点
} huffnode;
typedef struct
{
char cd[MAX];
int start; //叶节点的Huffman code的起始位置
} huffcode;
int main()
{
huffnode ht[2*MAX];
huffcode hcd[MAX], d;
int i, k , f, l, r, n, c, m1, m2;
cout << "元素个数:";
cin >> n;
for( i = 1; i <= n; i++ )
{
cout << "第" << i << "个元素=>\t节点值:";
cin >> ht[i].data;
cout << "\t\t权重:";
cin >> ht[i].weight;
}
//初始化Huffman叶节点和分支节点
//数组为静态存储空间,其最大的特征为 - 预先分配所需空间
for( i = 1; i <= 2*n-1; i++ )
{
ht[i].parent = ht[i].left = ht[i].right = 0;
}
//构造哈夫曼树
for( i = n+1; i <= 2*n-1; i++ )
{
m1 = m2 = 32767; //m1 - 最小权值,m2 - 次小权值
l = r = 0; //l - 最小权值节点的位置,r - 次小权值节点的位置
for( k = 1; k <= i-1; k++ )
{
if(ht[k].parent == 0)
{
if(ht[k].weight < m1)
{
m2 = m1; //更新最小、次小权值并记录其所在位置
r = l;
m1 = ht[k].weight;
l = k;
}
else if(ht[k].weight < m2)
{
m2 = ht[k].weight;
r = k;
}
}
}
//设置找到的2个子节点的父节点信息
ht[l].parent = i;
ht[r].parent = i;
ht[i].weight = ht[l].weight + ht[r].weight;
ht[i].left = l;
ht[i].right = r;
}
//根据Huffman树求Huffman编码
//注意:因为HuffmanCode的生成过程为自底向上,因此咱们这里采用-倒着存方式
for( i = 1; i <= n; i++ )
{
d.start = n+1; //start - 记录HuffmanCode的起始位置
c = i; //当前叶节点在结构数组中的下标
f = ht[i].parent; //当前叶节点的父节点
while( f != 0) //到达根节点则HuffmanCode创建完毕,循环退出
{
if( ht[f].left == c ) //若当前叶节点的父节点的左指针指向了当前叶节点,则记录0 - 左儿子
d.cd[--d.start] = '0';
else //若当前叶节点的父节点的右指针指向了当前叶节点,则记录1 - 右儿子
d.cd[--d.start] = '1';
c = f; //更新儿子节点(分支节点) - 自叶节点到根节点创建HuffmanCode
f = ht[f].parent; //更新当前儿子节点的父节点 直到根节点为止
}
hcd[i] = d;
}
//输出HuffmanCode
cout << "输出HuffmanCode:\n" ;
for( i = 1; i <= n; i++ )
{
cout << " " << ht[i].data << ": ";
for( k = hcd[i].start; k <= n; k++ )
cout << hcd[i].cd[k];
cout << endl;
}
return 0;
}
运行结果: