一、哈夫曼树的基本概念
哈夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,在实际中有广泛的用途。
- 路径: 从树中一个节点到另一个节点之间的分支构成这两个节点之间的路径。
- 路径长度: 路径上的分支数目称作路径长度。
- 树的路径长度: 从树根到每一叶子节点的路径长度之和。
- 权: 赋予某个实体的一个量,是对实体的某个或某些属性的数值化描述。在数据结构中,实体有节点(元素)和边(关系)两大类,所以对应有节点权和边权。节点权或边权具体代表什么意义,由具体情况决定。如果在一棵树中的节点上带有权值,则对应的就有带权树等概念。
- 节点的带权路径长度: 从该节点到树根之间的路径长度与节点上权值的乘积。
- 树的带权路径长度: 树中所有叶子节点的带权路径长度之和。
- 哈夫曼树: 假设有m个权值{w1, w2,…, wm},可以构造一棵含n个叶子节点的二叉树,每个叶子节点的权值为wi,则其中带权路径长度最小的二叉树称作最优二叉树或哈夫曼树。
二、哈夫曼树的构造
2.1 哈夫曼树的构造过程
2.2 构造哈夫曼树的算法实现
哈夫曼树是一种二叉树,由于哈夫曼树中没有度为1的节点,则一棵有n个叶子节点的哈夫曼树共有2n−1个节点,可以存储在一个大小为2n−1的一维数组中。树中每个节点还要包含其双亲信息和孩子节点的信息,由此,每个节点的存储结构设计如下:
typedef int DataType;
typedef struct HTNode
{
DataType weight;
int parent;
int lc, rc;
}*HuffmanTree;
哈夫曼树的各节点存储在由HuffmanTree定义的动态分配的数组中,为了实现方便,数组的0号单元不使用,从1号单元开始使用,所以数组的大小为2n。将叶子节点集中存储在前面部分的n个位置,而后面的n−1个位置存储其余非叶子节点。
接下来我们就要对HuffmanTree进行初始化并创建:
void Select(HuffmanTree& HT, int n, int& s1, int& s2)
{
int min;
for (int i = 1; i <= n; i++)
{
if (HT[i].parent == 0)
{
min = i;
break;
}
}
for (int i = min + 1; i <= n; i++)
{
if (HT[i].parent == 0 && HT[i].weight < HT[min].weight)
min = i;
}
s1 = min;
for (int i = 1; i <= n; i++)
{
if (HT[i].parent == 0 && i != s1)
{
min = i;
break;
}
}
for (int i = min + 1; i <= n; i++)
{
if (HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1)
min = i;
}
s2 = min;
}
void CreateHuff(HuffmanTree& HT, DataType* w, int n)
{
int m = 2 * n - 1;
HT = (HuffmanTree)calloc(m + 1, sizeof(HTNode));
for (int i = 1; i <= n; i++)
HT[i].weight = w[i - 1];
for (int i = n + 1; i <= m; i++)
{
int s1, s2;
Select(HT, i - 1, s1, s2);
HT[i].weight = HT[s1].weight + HT[s2].weight;
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lc = s1;
HT[i].rc = s2;
}
}
三、哈夫曼树编码
3.1哈夫曼树编码的认识
对一棵具有n个叶子的哈夫曼树,若对树中的每个左分支赋予0,对每个右分支赋予1,则从根到每个叶子的路径上,各分支的赋值分别构成一个二进制串,该二进制串就称为哈夫曼编码。
哈夫曼编码具有这样的性质:
- 哈夫曼编码是前缀编码
- 哈夫曼编码是最优前缀编码
3.2 哈夫曼树编码的实现
void HuffCoding(HuffmanTree& HT, HuffmanCode& HC, int n)
{
HC = (HuffmanCode)malloc(sizeof(char*) * (n + 1));
char* code = (char*)malloc(sizeof(char) * n);
code[n - 1] = '\0';
for (int i = 1; i <= n; i++)
{
int start = n - 1;
int c = i;
int p = HT[c].parent;
while (p)
{
if (HT[p].lc == c)
code[--start] = '0';
else
code[--start] = '1';
c = p;
p = HT[c].parent;
}
HC[i] = (char*)malloc(sizeof(char) * (n - start));
strcpy(HC[i], &code[start]);
}
free(code);
}