HUFFMAN(C)
N=|C|;
Q=C;
for i=1 to n-1
allocate a new node z
z.left=x=EXTRACT-MIN(Q);
z.right=y= EXTRACT-MIN(Q);
z.freq=x.freq+y.freq;
INSERT(Q,z);
return EXTRACT-MIN(Q);
二、算法分析
哈夫曼编码是一种无损压缩方法,其一般算法如下:
A、首先统计信源中各符号出现的概率,按符号出现的概率从大到小排序;
B、把最小的两个概率相加合并成新的概率,与剩余的概率组成新的概率集合;
C、对新的概率集合重新排序,再次把其中最小的两个概率相加,组成新的概率集合。如此重复进行,直到最后两个概率的和为l;
D、分配码字:码字分配从最后一步开始反向进行,对于每次相加的两个概率,给大的赋¨O",小的赋¨1¨(也可以全部相反,如果两个概率相等, 则从中任选一个赋¨O¨, 另一个赋"l¨即可),读出时由该符号开始一直走到最后的概率和¨1¨,将路线上所遇到的¨O¨和¨l¨按最低位到最高位的顺序排好,就是该符号的哈大曼编码。
例:设一幅灰度级为6(分别用a 1、a2、a3、a4、a5、a6表示)的图像中,各灰度所对应的概率分别为O.40、O.30、O.1 O、O.1 O、O.06、O.O4。现对其进行哈大曼编码,具体步骤如下:
(1)首 先对信源概率从大到小排序,选出最小的两个概率(O.06和O.04),相加得O.1,与其他概率组成新的概率集合(O.4,O.3,O.1,O.1,O.1);
(2)对新的概率集合重新排序,选出最小的两个概率(O.1和O.1),相加得O.2,组成新的概率集合(O.4,O.3,O.1,O.2);
(3)对新的概率集合重新排序(O.4,O.3,O.2,O.1),选出最小的两个概率(O.2和O.1),相加得O.3,组成新的概率集合(O.4,O.3,O.3);
(4)对新的概率集合重新排序,选出最小的两个概率(O.3和O.3),相加得O.6,直到最后两个概率(O.60和O.40)相加和为1;
(5)分配码字。从最后一步反向进行,首先给最后相加的两个概率(O.60和O.40)分配码字,由于O.60大于O.40,于是给O.60赋¨O¨,给O.40赋¨1¨。
(6)最后写出每个符号的哈夫曼编码。以符号a4(对应的概率为O.1)为例,在从O.1到1.O的路径上,它所遇到的赋值(¨O¨或¨1¨)依次为O、O、1、O,将其反向排列成¨O l OO",于是就形成了符号a4的哈大曼码字¨OlOO¨。
三、算法实现
// 哈夫曼编码(算法)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码
typedef struct
{
unsigned int weight; //用来存放各个结点的权值
unsigned int parent,LChild,RChild; //指向双亲、孩子结点的指针
} HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树
//选择两个parent为0,且weight最小的结点s1和s2
void Select(HuffmanTree *ht,int n,int *s1,int *s2)
{
int i,min;
for(i=1; i<=n; i++)
{
if((*ht)[i].parent==0)
{
min=i;
break;
}
}
for(i=1; i<=n; i++)
{
if((*ht)[i].parent==0)
{
if((*ht)[i].weight<(*ht)[min].weight)
min=i;
}
}
*s1=min;
for(i=1; i<=n; i++)
{
if((*ht)[i].parent==0 && i!=(*s1))
{
min=i;
break;
}
}
for(i=1; i<=n; i++)
{
if((*ht)[i].parent==0 && i!=(*s1))
{
if((*ht)[i].weight<(*ht)[min].weight) min=i;
}
}
*s2=min;
}
//构造哈夫曼树ht。w存放已知的n个权值
void CrtHuffmanTree(HuffmanTree *ht,int *w,int n)
{
int m,i,s1,s2;
m=2*n-1;
*ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(i=1; i<=n; i++) //1--n号存放叶子结点,初始化
{
(*ht)[i].weight=w[i];
(*ht)[i].LChild=0;
(*ht)[i].parent=0;
(*ht)[i].RChild=0;
}
for(i=n+1; i<=m; i++)
{
(*ht)[i].weight=0;
(*ht)[i].LChild=0;
(*ht)[i].parent=0;
(*ht)[i].RChild=0;
} //非叶子结点初始化
printf("\nHuffmanTree: \n");
for(i=n+1; i<=m; i++) //创建非叶子结点,建哈夫曼树
{ //在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0
//且weight最小的结点,其序号分别赋值给s1、s2
Select(ht,i-1,&s1,&s2);
(*ht)[s1].parent=i;
(*ht)[s2].parent=i;
(*ht)[i].LChild=s1;
(*ht)[i].RChild=s2;
(*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight;
printf("%d (%d, %d)\n",(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);
}
printf("\n");
} //哈夫曼树建立完毕
//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)
{
char *cd;
int i,start,p;
unsigned int c;
hc=(HuffmanCode *)malloc((n+1)*sizeof(char *)); //分配n个编码的头指针
cd=(char *)malloc(n*sizeof(char)); //分配求当前编码的工作空间
cd[n-1]='\0'; //从右向左逐位存放编码,首先存放编码结束符
for(i=1; i<=n; i++) //求n个叶子结点对应的哈夫曼编码
{
start=n-1; //初始化编码起始指针
for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent) //从叶子到根结点求编码
if( (*ht)[p].LChild==c) cd[--start]='0'; //左分支标0
else cd[--start]='1'; //右分支标1
hc[i]=(char *)malloc((n-start)*sizeof(char)); //为第i个编码分配空间
strcpy(hc[i],&cd[start]);
}
free(cd);
for(i=1; i<=n; i++)
printf("HuffmanCode of %3d is %s\n",(*ht)[i].weight,hc[i]);
printf("\n");
}
int main()
{
HuffmanTree HT;
HuffmanCode HC;
int *w,i,n,wei,m;
printf("\nn = " );
scanf("%d",&n);
w=(int *)malloc((n+1)*sizeof(int));
printf("\ninput the %d element's weight:\n",n);
srand((unsigned)time(NULL));
for(i=1; i<=n; i++)
{
printf("%d: ",i);
fflush(stdin);
wei=rand()%100;
printf("%d",wei);
printf("\n");
w[i]=wei;
}
CrtHuffmanTree(&HT,w,n);
CrtHuffmanCode(&HT,&HC,n);
return 0;
}