先简单回顾一下哈夫曼二叉树的概念:哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的带权路径长度记为WPL=(W1*L1+W2*L2+W3*L3+...+ Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。
构造哈夫曼树的算法如下:
1)对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F={T1,T2,T3,...,Ti,..., Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。
2)在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
3)从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
4)重复2)和3),直到集合F中只有一棵二叉树为止。
下面举一个很普通的例子:
对于权2,3,5,7,11,13,17,19,23,29,31,37,41,求具有最小带权外部路径长度的二叉树.
1>找到最小权值和次小权值2和3,然后2+3=5构成两个节点的父母,并将5作为下次合并的新权值;
5
/ \
2 3
2>此时权值更新为5,5,7,11,13,17,19,23,29,31,37,41;然后按照1>的模式找到5和5 并产生其父母10,并作为下次合并的新权值;
10
/ \
5 5
/ \
2 3
3>同理 10,7,11,13,17,19,23,29,31,37,41;
17
/ \
7 10
/ \
5 5
/ \
2 3
4> 17,11,13,17,19,23,29,31,37,41;
此时注意最小数和次小树不再是前两位,所以要另起一棵树,由11 和13构成;
17
/\
7 10
24 /\
/\ 5 5
11 13 /\
2 3
5>好了依次比到最后:
17,17,24,19,23,29,31,37,41;
19,24,23,29,31,34,37,41;
24,29,31,34,37,41,42;
31,34,37,41,42,53;
37,41,42,53,65;
42,53,65,78;
65,78,95;
95,143;
238;
完整的树图:
238
/ \
95 143
/ \ / \
42 53 65 78
/\ / \ / \ / \
19 23 24 29 31 34 37 41
/ \ / \
11 13 17 17
/ \
7 10
/ \
5 5
/\
2 3
对于哈夫曼树,有一个很重要的定理:对于具有n个叶子节点的哈夫曼树,共有2*n-1个节点。
构造哈夫曼树的算法的实现原理如下:对于n个叶子节点,我们根据上面的定理构造出大小为2*n-1的数组来存放整个哈夫曼树。这个数组的前n个位置存放的为已知的叶子节点,后(n-1)个位置存放的为动态生成的树内节点。在算法的大循环过程中,要做的事情就是根据位置i前面的已知节点(或者是叶节点或者是生成的树内节点),找出 parent为-1(即节点尚且是一个子树的根结点)的节点中权值最小的两个节点,然后根据这两个节点构造出位置为i的新的父节点(也就是一棵新树的根结点)。
而哈弗曼编码也类似于哈弗曼树的构建,只是要构建一串010101010111…….其核心就是使频率越高的码元采用越短的编码。编码过程就根据不同码元的频率(相当于权值)构造出哈夫曼树,然后求叶子节点到根节点的路径,其中节点的左孩子路径标识为0,右孩子路径标识为1。
下面给出Huffman算法的完整代码:
#include <stdio.h>
#define n 4
typedef struct
{
int parent;
int lchild,rchild;
int weight;
int flag;
}Node;
typedef struct
{
char bit[n];
int start;
char ch;
}CodeNode;
Node haffman[7];
CodeNode code[n];
int select(int j)
{
int i,position;
int Min=100;
for (i=0;i<=j;i++)
if (haffman[i].weight<Min && haffman[i].flag==-1)
{
Min=haffman[i].weight;
position=i;
}
haffman[position].flag=1;
return position;
}
void haffmanCode()
{
int i,j,p,k;
for (i=0;i<n;i++)
{
printf("%d ",haffman[i].weight);
code[i].start=n-1;
j=i;
p=haffman[i].parent;
while (p!=-1)
{
if (haffman[p].lchild==j)
code[i].bit[code[i].start]='0'; //左0右1;
else
code[i].bit[code[i].start]='1';
code[i].start--;
j=p;
p=haffman[p].parent;
}
for(k=code[i].start+1 ;k<n ;k++)
printf("%c",code[i].bit[k]);
printf("\n");
}
}
int main()
{
int max=100 , i ;
int m1,m2;
//初始化节点数据;
for (i=0;i<2*n-1;i++)
{
haffman[i].weight=0;
haffman[i].parent=-1;
haffman[i].lchild=-1;
haffman[i].rchild=-1;
haffman[i].flag=-1;
}
printf("请输入叶子节点的权值:");
for (i=0;i<n;i++)
{
scanf("%d",&haffman[i].weight);
}
//构造哈弗曼二叉树(n-1次合并);
for (i=n ; i<2*n-1 ;i++)
{
m1=select(i-1); //最小权值位;
m2=select(i-1); //次最小权值;
haffman[m1].parent=i;
haffman[m2].parent=i; //父节点在数组中的位置;
haffman[i].weight=haffman[m1].weight+haffman[m2].weight;
haffman[i].lchild=m1;
haffman[i].rchild=m2; //儿子节点在数组中的位置;
}
for (i=0 ;i<2*n-1 ;i++)
printf("%d",haffman[i].weight);
printf("\n");
haffmanCode();
}
#define n 4
typedef struct
{
int parent;
int lchild,rchild;
int weight;
int flag;
}Node;
typedef struct
{
char bit[n];
int start;
char ch;
}CodeNode;
Node haffman[7];
CodeNode code[n];
int select(int j)
{
int i,position;
int Min=100;
for (i=0;i<=j;i++)
if (haffman[i].weight<Min && haffman[i].flag==-1)
{
Min=haffman[i].weight;
position=i;
}
haffman[position].flag=1;
return position;
}
void haffmanCode()
{
int i,j,p,k;
for (i=0;i<n;i++)
{
printf("%d ",haffman[i].weight);
code[i].start=n-1;
j=i;
p=haffman[i].parent;
while (p!=-1)
{
if (haffman[p].lchild==j)
code[i].bit[code[i].start]='0'; //左0右1;
else
code[i].bit[code[i].start]='1';
code[i].start--;
j=p;
p=haffman[p].parent;
}
for(k=code[i].start+1 ;k<n ;k++)
printf("%c",code[i].bit[k]);
printf("\n");
}
}
int main()
{
int max=100 , i ;
int m1,m2;
//初始化节点数据;
for (i=0;i<2*n-1;i++)
{
haffman[i].weight=0;
haffman[i].parent=-1;
haffman[i].lchild=-1;
haffman[i].rchild=-1;
haffman[i].flag=-1;
}
printf("请输入叶子节点的权值:");
for (i=0;i<n;i++)
{
scanf("%d",&haffman[i].weight);
}
//构造哈弗曼二叉树(n-1次合并);
for (i=n ; i<2*n-1 ;i++)
{
m1=select(i-1); //最小权值位;
m2=select(i-1); //次最小权值;
haffman[m1].parent=i;
haffman[m2].parent=i; //父节点在数组中的位置;
haffman[i].weight=haffman[m1].weight+haffman[m2].weight;
haffman[i].lchild=m1;
haffman[i].rchild=m2; //儿子节点在数组中的位置;
}
for (i=0 ;i<2*n-1 ;i++)
printf("%d",haffman[i].weight);
printf("\n");
haffmanCode();
}
来源:http://www.cnblogs.com/gavindlutsw/archive/2011/08/07/Huffman.html