基本概念
a、路径和路径长度
若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径。
从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1.
b、结点的权和带权路径长度
在许多应用中,常常将树中的结点赋予一个有着某种意义的实数,我们称此实数为该结点的权,(如下面一个树中的蓝色数字表示结点的权)
结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘积。
c、树的带权路径长度
树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,公式为:
其中,n表示叶子结点的数目,wi 和 li 分别表示叶子结点 ki 的权值和树根结点到 ki 之间的路径长度。
如下图中树的带权路径长度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4 = 122
d、哈夫曼树
哈夫曼树又称最优二叉树。它是 n 个带权叶子结点构成的所有二叉树中,带权路径长度 WPL 最小的二叉树。
如下图为一哈夫曼树示意图。
实现哈夫曼树编码的算法可分为两大部分:
(1)构造哈夫曼树;
(2)在哈夫曼树上求叶结点的编码;
哈夫曼树构造算法:
(1)由给定的n个权值构造n棵只有一个叶结点的二叉树,
从而得到一个二叉树的集合F = {T1,T2, … ,TN}
(2)在F中选取根结点的权值最小和次小的两棵二叉树作为左,右子树构造一棵新的二叉树,这棵二叉树根结点的权值为其左右子树权值之和
(3)在集合F中删除作为左右子树的两棵二叉树,并将建立的二叉树加入到集合F 中
(4)重复(2)(3)直到F中只剩下最后一棵所需的二叉树,就是哈夫曼树
在哈夫曼树上求叶结点的编码算法;
在已建立的哈夫曼树中,从叶结点开始,沿结点的双亲链回退到根结点,每回退一步,就走过了哈夫曼树的一个分支,从而得到一位哈夫曼的值
规定哈夫曼树值的左分支代表0,有分支代表1
如:对下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:
注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中的每个结点左子树的根结点小于等于右子树根结点的权
具体算法:
在这里插入代码片
#include<stdio.h>
#define MAXVALUE 10000 // 最大权值
#define MAXBIT 20 // 哈夫曼编码最大程度
typedef stuct
{
char ch;
int weight;
int parent;
int Lchild, Rchild;
}Htreetype;
typedef struct
{
int bit[100]:
int start; // 位串
char ch; // 编码在位串中的起始位置
}Hcodetype;
void select(Htreetype t[], int k, int *p1, int *p2) /// 选择权值最小的结点
{
*p1 = *p2 = 0;
int small1, smasll2;
smasll1 = small2 = MAXVALUE
int i;
for (i = 0; i < k; i++)
{
if (t[i[.parent == -1 )
{
if (t[i].weight < small1)
{
small2 = small1;
small1 = t[i].weight;
*p2 = *p1;
*p2 = i;
}
else if ( t[i].weight < small2 )
{
small2 = t[i].weight;
*p2 = i;
}
}
}
}
void HuffmanTree(Htreetype t[]) // 构造哈夫曼树
{
int i, j, p1, p2;
double f;
p1 = p2 = 0;
char c;
for ( i = 0; i < m; i++) // 初始化
{
t[i].weight = 0;
t[i].Lchild = -1;
t[i].parent = -1;
t[i].Rchild = -1;
}
for ( i = 0; i < n; i++)
{
printf("请输入第%d个字符和权值‘,’分隔", i+1);
scanf("%c,%lf", &c,&f);
getchar();
t[i].ch = c;
t[i].weight = f;
}
for (i = n; i < m; i++) // 构造哈夫曼树
{
select(t, i, &p1, &p2);
t[p1].parent = i;
t[p2].parent = i;
t[i].Lchild = p1;
t[i].Rchild = p2;
t[i].weight = t[p1].weight + t[p2].wieght;
}
void HuffmanCode(Hcodetype code[], Htreetype t[])
{
int i, c, p;
Hcodetype cd; // 缓冲变量, 占时存储
HuffmanTree(t);
for (i = 0; i < n ; i++)
{
cd.start = n;
cd.ch = t[i].ch;
c = i; // 从叶子结点向上
p = t[i].parent; // t[p]是t[i]是双亲
while (p != -1)
{
cd.start--;
if (t[p].Lchild == c)
cd.bit[cd.start] = '0'; // 左知识编为0;
else
cd.bit[cd.start] = '1' // 右子树编为1;
c = p; // 移动
p = t[c].parent;
}
code[i] = cd; // 第i + 1个字符的编码存入code
}
}
void show(Htreetype t[], Hcodetype code[])
{
int i, j;
for (i = 0; i < n; i++)
{
printf("%c: ", code[i].cd);
for (j = code[i].start; j < n; j++)
{
printf("%c ", code[i].bit[j]);
}
printf("\n");
}
}
int main()
{
printf("请输入字符数:\n");
scanf("%d", &n);
getchar();
m = 2 * n - 1;
Htreetype t[m];
Hcodetype code[n];
HuffmanCode(code, t);
show(t, code);
return 0;
}
运行结果如图: