哈弗曼树与哈弗曼编码简介
树和哈夫曼编码。哈夫曼编码是哈夫曼树的一个应用。哈夫曼编码应用广泛,如JPEG中就应用了哈夫曼编码。 首先介绍什么是哈夫曼树。哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的 路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的带权路径长度记为
WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。
哈夫曼编码步骤:
一、对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F= {T1,T2,T3,...,Ti,...,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。)
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
简易的理解就是,假如我有A,B,C,D,E五个字符,出现的频率(即权值)分别为5,4,3,2,1,那么我们第一步先取两个最小权值作为左右子树构造一个新树,即取1,2构成新树,其结点为1+2=3,如图:
虚线为新生成的结点,第二步再把新生成的权值为3的结点放到剩下的集合中,所以集合变成{5,4,3,3},再根据第二步,取最小的两个权值构成新树,如图:
再依次建立哈夫曼树,如下图:
其中各个权值替换对应的字符即为下图:
按照左子树0,右子树1,遍历整棵树,就可以将各叶子节点对应的编码写出来:
所以各字符对应的编码为:A->11, B->10, C->00, D->011, E->010
霍夫曼编码是一种无前缀编码。解码时不会混淆。其主要应用在数据压缩,加密解密等场合。
C语言代码实现:
#include <string.h>
#include <stdio.h>
#define MAXVALUE 10000 /*定义最大权值*/
#define MAXLEAF 30 /*定义哈夫曼树叶结点个数*/
#define MAXNODE MAXLEAF*2 -1
#define MAXBIT 100 /*定义哈夫曼编码的最大长度*/
//--------------------------------------------------------------------//
typedef struct
{
int bit[MAXBIT];
int start;
} HCodeType;
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
int value;
} HNodeType;
//--------------------------------------------------------------------//
//C语言实现哈弗曼树编码
typedef struct
{
int bit[MAXBIT];
int start;
} HCODETYPE;
typedef struct
{
int weight;
int parent;
int lchild;
int rchild;
} HNODETYPE;
char *getcode1(char *s1, char *s2, char *s3) /*首先去掉电文中的空格*/
{
char temp[128] = "", *p, *q;
p = s1;
while ((q = strstr(p, s2)) != NULL)
{
*q = '\0';
strcat(temp, p);
strcat(temp, s3);
p = q + strlen(s2);
}
strcat(temp, p);
strcpy(s1, temp);
return s1;
}
/*再去掉重复出现的字符(即压缩电文),提取哈夫曼树叶结点*/
char * getcode(char *s1)
{
char s2[26], s5[26];
char temp[200] = "", *p, *q, *r, *s3 = "";
int m, e, n = 0;
m = strlen(s1);
while (m > 0)
{
p = s1;
s2[0] = s1[0];
s2[1] = '\0';
r = s2;
e = 0;
while ((q = strstr(p, r)) != NULL)
{
*q = '\0';
strcat(temp, p);
strcat(temp, s3);
p = q + strlen(s2);
e++;
}
m -= e;
strcat(temp, p);
strcpy(s1, temp);
s5[n] = s2[0];
n++;
strcpy(temp, "");
}
s5[n] = '\0';
strcpy(s1, s5);
printf(" 压缩后的电文(即叶结点): %s\n", s1);
return s1;
}
HNODETYPE huffmantree(char *s2, char s3[])
{
HNODETYPE huffnode[MAXNODE];
HCODETYPE huffcode[MAXLEAF], cd;
int sum, i, j, n1, n2, x1, x2, p, k, c;
char s1[26] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z' };
char s5[MAXLEAF];
int ww[26] = { 0 }, n = 0;
strcpy(s5, s2);
sum = strlen(s2);
for (i = 0; i < 26; i++) /*统计所有字符出现的频度*/
{
for (j = 0; j < sum; j++)
{
if (s2[j] == s1[i])
{
ww[i]++;
}
}
}
n = strlen(s3);
for (i = 0; i < 2 * n - 1; i++)
{
huffnode[i].weight = 0;
huffnode[i].parent = -1;
huffnode[i].lchild = -1;
huffnode[i].rchild = -1;
}
for (i = 0; i < n; i++) /*分配给各叶结点权值*/
{
for (j = 0; j < 26; j++)
{
if (s3[i] == s1[j])
{
huffnode[i].weight = ww[j];
}
}
}
for (i = 0; i < n - 1; i++) /*构造哈夫曼树*/
{
n1 = n2 = MAXVALUE;
x1 = x2 = 0;
for (j = 0; j < n + i; j++)
{
if (huffnode[j].parent == -1 && huffnode[j].weight < n1)
{
n2 = n1;
x2 = x1;
n1 = huffnode[j].weight;
x1 = j;
}
else if (huffnode[j].parent == -1 && huffnode[j].weight < n2)
{
n2 = huffnode[j].weight; x2 = j;
}
}
//
huffnode[x1].parent = n + i;
huffnode[x2].parent = n + i;
huffnode[n + i].weight = huffnode[x1].weight + huffnode[x2].weight;
huffnode[n + i].lchild = x1;
huffnode[n + i].rchild = x2;
}
for (i = 0; i < n; i++) /*求每个叶结点的哈夫曼编码*/
{
cd.start = n - 1;
c = i;
p = huffnode[c].parent;
while (p != -1)
{
if (huffnode[p].lchild == c)
{
cd.bit[cd.start] = 0;
}
else
{
cd.bit[cd.start] = 1;
}
cd.start--;
c = p;
p = huffnode[c].parent;
}
for (j = cd.start + 1; j < n; j++)
{
huffcode[i].bit[j] = cd.bit[j];
}
huffcode[i].start = cd.start;
}
printf(" 各叶结点对应哈夫曼编码 : ");/*输出每个叶结点的哈夫曼编码*/
for (i = 0; i < n; i++)
{
for (j = huffcode[i].start + 1; j < n; j++)
{
printf("%d", huffcode[i].bit[j]);
}
printf(" ");
}
printf("\n 电文的全部哈夫曼编码 : ");/*输出电文的全部哈夫曼编码*/
for (k = 0; k < sum; k++)
{
for (i = 0; i < n; i++)
{
if (s2[k] == s3[i])
{
for (j = huffcode[i].start + 1; j < n; j++)
{
printf("%d", huffcode[i].bit[j]);
}
printf(" ");
}
}
}
printf("\n");
}
void main1()
{
char s1[MAXLEAF], s2[MAXLEAF];
printf("\n 请输入电文 : ");
//gets(s1);
strcpy(s1, "a b c d e");
strcpy(s2, getcode1(s1, " ", ""));
huffmantree(s1, getcode(s2));
}
//--------------------------------------------------------------------//
//--------------------------------------------------------------------//
//
void HuffmanTree(HNodeType HuffNode[MAXNODE], int n)
{
int i, j, m1, m2, x1, x2;
char nvalue[5] = { 'A','B','C','D','E' };//叶子节点n
int qvalue[5] = {5, 4, 3, 2, 1};
//总共树的节点为2n-1
for (i = 0; i < 2 * n - 1; i++)
{
HuffNode[i].weight = 0;//权值
HuffNode[i].parent = -1;
HuffNode[i].lchild = -1;
HuffNode[i].rchild = -1;
HuffNode[i].value = i; //实际值,可根据情况替换为字母
}
for (i = 0; i < n; i++)
{
//printf("Please input weight of leaf node %d: \n", i);
//scanf("%d", &HuffNode[i].weight);
HuffNode[i].weight = n - i;
HuffNode[i].value = nvalue[i];
printf("the weight of leaf node %d is %d: \n", i, HuffNode[i].weight);
}
for (i = 0; i < n - 1; i++)//构造哈弗曼树
{
m1 = m2 = MAXVALUE;
x1 = x2 = 0;
for (j = 0; j < n + i; j++) //找出最小的节点权重
{
if (HuffNode[j].weight < m1 && HuffNode[j].parent == -1)
{
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j;
}
else if (HuffNode[j].weight < m2 && HuffNode[j].parent == -1)
{
m2 = HuffNode[j].weight;
x2 = j;
}
}
HuffNode[x1].parent = n + i;
HuffNode[x2].parent = n + i;
HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight;
HuffNode[n + i].lchild = x1;
HuffNode[n + i].rchild = x2;
printf("x1.weight and x2.weight in round %d: %d, %d\n", i + 1, HuffNode[x1].weight, HuffNode[x2].weight);
//printf("\n");
}
}
//解码
void decodeing(char string[], HNodeType Buf[], int Num)
{
int i, tmp = 0, code[1024];
int m = 2 * Num - 1;
char *nump;
char num[1024];
for (i = 0; i < strlen(string); i++)
{
if (string[i] == '0')
{
num[i] = 0;
}
else
{
num[i] = 1;
}
}
i = 0;
nump = &num[0];
while (nump < (&num[strlen(string)]))
{
//从根节点遍历哈弗曼树
tmp = m - 1;
while ((Buf[tmp].lchild != -1) && (Buf[tmp].rchild != -1))
{
if (*nump == 0)
{
tmp = Buf[tmp].lchild;
}
else
{
tmp = Buf[tmp].rchild;
}
nump++;
}
printf("%d(%c)", Buf[tmp].value, Buf[tmp].value);
}
}
int main2(void)
{
HNodeType HuffNode[MAXNODE];
HCodeType HuffCode[MAXLEAF], cd;
int i, j, c, p, n;
char pp[100];
//printf("Please input n:\n");
//scanf("%d", &n);
n = 5;
printf("the node num of huffman tree is %d\n", n);
//哈弗曼树
HuffmanTree(HuffNode, n);
//哈夫曼编码
for (i = 0; i < n; i++)
{
cd.start = n - 1;
c = i;
p = HuffNode[c].parent;
while (p != -1)
{
if (HuffNode[p].lchild == c) {
cd.bit[cd.start] = 0;
} else {
cd.bit[cd.start] = 1;
}
//
cd.start--;
c = p;
p = HuffNode[c].parent;
}
for (j = cd.start + 1; j < n; j++)//
{
HuffCode[i].bit[j] = cd.bit[j];
}
HuffCode[i].start = cd.start;
}
for (i = 0; i < n; i++)//
{
printf("%d 's Huffman code is: ", i);
for (j = HuffCode[i].start + 1; j < n; j++)
{
printf("%d", HuffCode[i].bit[j]);
}
printf(" start:%d", HuffCode[i].start);
printf("\n");
}
printf("\n");
printf("Decoding? Please Enter code:\n");
scanf("%s", &pp);
decodeing(pp, HuffNode, n);
getchar();
return 0;
}
运行结果:
上一篇: 哈弗曼编码译码器