哈夫曼树与哈夫曼编码
哈夫曼树(最优二叉树):带权路径长度最短的树
基本概念:路径、结点的路径长度、树的路径长度:从根结点到每一个结点的路径长度之和。
权:给结点赋值
结点的带权路径长度:从根结点到该结点之间的路径长度与该结点的权的乘积。
树的带权路径长度:树的所有叶子结点的带权路径长度之和。
构造方法:
(1)根据n个给定的权值{W1,W2,...,Wn}构成n棵二叉树的森林F={T1,T2,...,Tn},其中Ti只有一个带权为Wi的根结点(构造森林全是根)
(2)在F中选取两棵根结点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。(选用两小造新树)
(3)在F中删除这两棵树,同时将新得到的二叉树加入森林中。(删除两小添新人)
(4)重复(2)和(3),直到森林中只有一棵树为止,这棵树即为哈夫曼树。(重复2、3剩单根)
哈夫曼树构造算法的实现:
/* 输入: 5 29 7 8 14 23 3 11
输出: 8 15 19 29 42 58 100 * /
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int weight;
int parent,lch,rch;
}HTNode,*HuffmanTree;
void Select(HuffmanTree HT,int i,int *s1,int *s2)
{
int j,m,n;
m = 0;
int flag;
flag = 10000;
for(j=1;j<=i;j++)
{
if((flag >= HT[j].weight)&&(HT[m].parent==0&&HT[j].parent==0))
{
m = j;
flag = HT[m].weight;
}
}
n = 0;
flag = 10000;
for(j=1;j<=i;j++)
{
if((flag >= HT[j].weight)&&(HT[n].parent==0&&HT[j].parent==0)&&j!=m)
{
n = j;
flag = HT[n].weight;
}
}
*s1 = m;
*s2 = n;
printf("%d %d\n",*s1,*s2);
}
void CreateHuffmanTree(HuffmanTree HT,int n)
{
//初始化
if(n<1) return;
int m = 2*n-1;
int i;
for(i=1;i<=m;i++)
{
HT[i].lch = 0;
HT[i].rch = 0;
HT[i].parent = 0;
}
for(i=1;i<=n;i++)
scanf("%d",&HT[i].weight);
//构造哈夫曼树
for(i=n+1;i<=m;i++)
{
int s1,s2;
Select(HT,i-1,&s1,&s2); //在HT[k](1<=k<=i-1)中选择2个parent为0,且权值最小
HT[s1].parent = i,HT[s2].parent = i; //的结点,返回序号s1,s2
HT[i].lch=s1,HT[i].rch=s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
}
int main()
{
HuffmanTree H;
int n,i;
n=8;
int m = 2*n-1;
H = (HuffmanTree)malloc(sizeof(HTNode)*(m+1));
CreateHuffmanTree(H,n);
for(i=1;i<=m;i++)
printf("%d %d %d %d\n",H[i].weight,H[i].parent,H[i].lch,H[i].rch);
return 0;
}
哈夫曼编码思想:出现次数较多的字符使用较短的编码,任意字符的编码都不是另一个字符编码的前缀。
(1)统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)
(2)利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
(3)在哈夫曼的每个上标0或1:
结点的左分支标0,右分支标1
将从根到每个叶子结点的路径上的标号连接起来,作为该叶子代表的字符的编码。
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct{ int weight; int parent,lch,rch; }HTNode,*HuffmanTree; void Select(HuffmanTree HT,int i,int *s1,int *s2) { int j,m,n; m = 0; int flag; flag = 10000; for(j=1;j<=i;j++) { if((flag >= HT[j].weight)&&(HT[m].parent==0&&HT[j].parent==0)) { m = j; flag = HT[m].weight; } } n = 0; flag = 10000; for(j=1;j<=i;j++) { if((flag >= HT[j].weight)&&(HT[n].parent==0&&HT[j].parent==0)&&j!=m) { n = j; flag = HT[n].weight; } } *s1 = m; *s2 = n; printf("%d %d\n",*s1,*s2); } void CreateHuffmanTree(HuffmanTree HT,int n) { //初始化 if(n<1) return; int m = 2*n-1; int i; for(i=1;i<=m;i++) { HT[i].lch = 0; HT[i].rch = 0; HT[i].parent = 0; } for(i=1;i<=n;i++) scanf("%d",&HT[i].weight); //构造哈夫曼树 for(i=n+1;i<=m;i++) { int s1,s2; Select(HT,i-1,&s1,&s2); //在HT[k](1<=k<=i-1)中选择2个parent为0,且权值最小 HT[s1].parent = i,HT[s2].parent = i; //的结点,返回序号s1,s2 HT[i].lch=s1,HT[i].rch=s2; HT[i].weight = HT[s1].weight + HT[s2].weight; } } void CreatHuffmanCode(HuffmanTree HT,char HC[10][10],int n) { char *cd; cd = (char *)malloc(sizeof(char)*n); cd[n-1] = '\0'; int i; for(i=0;i<=n;i++) { int start = n-1; int c = i; int f = HT[i].parent; while(f!=0){ start--; if(HT[f].lch == c) cd[start] = '0'; else cd[start] = '1'; c = f; f = HT[f].parent; } strcpy(HC[i],&cd[start]); } free(cd); } int main() { HuffmanTree H; int n,i; n=7; int m = 2*n-1; H = (HuffmanTree)malloc(sizeof(HTNode)*(m+1)); CreateHuffmanTree(H,n); for(i=1;i<=m;i++) printf("%d %d %d %d\n",H[i].weight,H[i].parent,H[i].lch,H[i].rch); char HC[10][10]; CreatHuffmanCode(H,HC,n); for(i=0;i<n;i++) printf("%s\n",HC[i]); return 0; }
文件的编码和译码: