代码改变世界

Huffman编码树

2011-08-07 22:00  gavin's world  阅读(431)  评论(0编辑  收藏  举报
先简单回顾一下哈夫曼二叉树的概念:哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为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();
}