目录点这里:【数据结构与算法】相关文章目录
一 . 基本概念:
赫夫曼树:给定带权的N个叶子构成的所有二叉树中,树的带权路径长度最小的二叉树(最优二叉树)
带权路径长度:所有树叶到树根之间的路径长度与该节点上权的乘积
权:赋予节点的有意义的参数
二 . 赫夫曼树的构造
例:设权值集合{2,4,5,7}
1.根据权值进行排序,取最小的两个叶子(2,4),较小的在左,较大的在右。添加一个新节点,节点的权值为它俩的权值和,构成一颗二叉树:
2.用得到的节点作为新的叶子,重复第一步:
3.一直重复构造,直到所有子叶都取完
所以共需要进行n-1次构造,总共有 n+n-1 = 2n -1 个节点;
可以证明,这样构造的二叉树是带权路径长度最小的二叉树
三 . 赫夫曼编码
1.数据压缩(编码):把文件中的每个字符转换成一个唯一的二进制位串,且串里面不能包含其它字符的表示串。
2.赫夫曼编码方法:如上例,设权为字符在文档中的重复次数,权值集合为{A:2,B:4,C:5,D:7},从根节点开始,向左是0,向右是1
那么
A的编码为:110。
B的编码为:111。
C的编码为:10。
D的编码为:0。
3.代码实现:(C语言)
头文件等:
#include <stdio.h>
#include <stdlib.h>
#define MAXINT 32767;
typedef struct
{
char Character;
unsigned int Weight;//权重
unsigned int Parent, Lchild, Rchild;//双亲节点,左子节点,右子节点
}HTNode;
a.生成霍夫曼树:
// n : 总叶子数 m: 用于构造Huffman树的数组大小(2n-1)
//k, j : 循环变量
//C, W : 接收字符和权时的中转变量
//Min_w1, Min_w2 : 用于存储两个最小权
//p1, p2 : 存储两个最小权数组的下标
void Great_Huffman(unsigned int n, HTNode HT[])
{
int k , j;
unsigned int W;
char C;
int m = 2*n - 1;
//输入n个字符及其权
for(k = 0; k < n; k++)
{
printf("input char and Weight : ");
while((C = getchar()) == '\n');
scanf("%d", &W);
HT[k].Character = C;
HT[k].Weight = W;
HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0;//初始化
}
//初始化之后用于连接子叶的n-1个节点
for(k = n; k < m; k++)
{
HT[k].Character = '\0';
HT[k].Weight = 0;
HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0;
}
//构造赫夫曼树
int Min_w1, Min_w2;
int p1, p2;
//外层循环用于构造新的节点
for(k = n; k < m; k++)
{
Min_w1 = Min_w2 = MAXINT;//初始化最小值
p1 = p2 = 0;//初始化下标
//内层循环用于寻找构造新节点的两个最小权节点
for(j = 0; j < k; j++)
{
if(HT[j].Parent == 0)//如果这个子叶尚未合并
{
if(HT[j].Weight < Min_w1)//更新最小权
{
Min_w2 = Min_w1;
p2 = p1;
Min_w1 = HT[j].Weight;
p1 = j;
}
else if(HT[j].Weight < Min_w2)//更新次小权
{
Min_w2 = HT[j].Weight;
p2 = j;
}
}
}
HT[p1].Parent = HT[p2].Parent = k;
HT[k].Lchild = p1; HT[k].Rchild = p2;
HT[k].Weight = HT[p1].Weight + HT[p2].Weight;
}
}
(注意输入格式,字符和权用空格隔开)
:
b.生成霍夫曼编码
//k : 循环变量
//sp : 编号指针,指向目前编号位,从后往前
//fp :双亲的下标
//p : 当前叶子下标
//HF : 暂时存储当前子叶的赫夫曼编码
void Huffman_Coding(unsigned int n, HTNode HT[])
{
int k;
int sp, fp;
char *HF;//当前编码
HF = (char *)malloc(n*sizeof(char));
int p;
for(k = 0; k < n; k++)
{
sp = n-1; p = k; fp = HT[k].Parent;
//从叶子搜索直到根节点
while(fp != 0)
{
if(HT[fp].Lchild == p)//如果当前节点是左子树
HF[sp] = '0';
else
HF[sp] = '1';
sp--;
fp = HT[fp].Parent;
p = fp;
}
//显示编码
printf("\n%c : ", HT[k].Character);
sp++;
while(sp < n)
{
printf("%c",HF[sp]);
sp++;
}
}
}