赫夫曼编码

霍夫曼算法


对于给出的一组权w={5,6,8,12},通过霍夫曼算法求出的扩充二叉树的带权外部路径为——?

1.有关霍夫曼树的相关概念

霍夫曼树:指所有叶子结点的二叉树中带权路径长度最小的二叉树。
节点的带权路径长度:从树的根节点到该节点的路径长度与该节点权的乘积。
树的带权路径长度:树中所有叶子结点的带权路径长度之和。

2.霍夫曼算法

(1)根据给定的n个权值{w1,w2,...,wn}构造n棵二叉树的集合F={T1,T2,...,Tn},其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树均空。
(2)在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
(3)在F中删除这两棵树,同时将新得到的二叉树加入F中。
(4)重复(2)和(3),直到F中只含一棵树为止。这棵树便是最优二叉树。
简单点说, 路径求法是这样的.先从这组权值中选取最小的两个结点如5和6组成新树,父结点W=11,将11加入权值中并去掉5和6,w={11,8,12},然后又选取最小的两个结点11和8,组成新树,父结点值为19加入权值中并去掉11和8,w={19,12}.直到最后根结点W=31.
这个时候将所有叶子结点和它的路径长度相乘再进行累加

所以是5*3+6*3+8*2+12*1 = 61



#include  <string.h>
#include  "global.h"
#define  MAX  10000;
typedef  struct
{
         unsigned  int  weight;
         unsigned  int  parent,lchild,rchild;
} HTNode ,*  HuffmanTree ;       // 动态分配数组存储赫夫曼树
typedef  char **  HuffmanCode ; //动态分配数组存储赫夫曼编码表
void   Select( HuffmanTree  HT, int  i,  int  &s1, int  &s2);
void  rootstartHuffmancode( HuffmanTree  &HT, HuffmanCode  &HC,  int  n);
void  leafstartHuffmancode( HuffmanTree  &HT, HuffmanCode  &HC,  int  n);

void  Huffmancoding( HuffmanTree  & HT ,  HuffmanCode  & HC  , int  *  w , int  n )
//w存放n个字符的权值,构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC
{
         if ( n  <=1)  return  ;
         int  i,s1,s2;
         int  m= n  *2-1;  
         HT =( HuffmanTree  )malloc((m+1)* sizeof (  HTNode ));
         HT [0].weight= MAX  ;    //s1,s2的初值设为0,此处为Select函数做铺垫
         for ( i=1;i<= n  ;i++)   //HT前n个分量表示叶子结点,最后一个分量表示根基点
       {                    //0号单元未用,初始化权值
                HT [i].lchild= NULL  ;
                HT [i].parent= NULL  ;
                HT [i].rchild= NULL  ;
                HT [i].weight=* w  ++;
       }
         for (i= n  +1;i<=m;i++) //初始化暂时未用空间
       {
                HT [i].lchild= NULL  ;
                HT [i].parent= NULL  ;
                HT [i].rchild= NULL  ;
       }
         for (i= n  +1;i<=m;i++)
       {    //在HT[1..i-1]选择parent为0且weight最小的两个结点,序号为s1和s2。
              Select(  HT ,i-1,s1,s2);
                HT [s1].parent=i;      HT [s2].parent=i;
                if (s1<s2) //这里的if是为了同样的深度,序号小的为左结点,仅仅是为了和书上一致,无意义。
                     {  HT [i].lchild=s1;     HT [i].rchild=s2;}                 
                else { HT  [i].lchild=s2;        HT [i].rchild=s1;}
                HT [i].weight= HT  [s1].weight+ HT [s2].weight;
       }
       
       rootstartHuffmancode(  HT , HC  , n );
         //leafstartHuffmancode(HT,HC,n);
}

         //------- 从叶子到根逆向求每个字符的赫夫曼编码---------//
void  leafstartHuffmancode( HuffmanTree  & HT ,  HuffmanCode  & HC  , int  n )
{
         HC =( HuffmanCode  )malloc(( n +1)*  sizeof ( char  *)); //分配n个字符编码的头指针向量
         char  *cd=( char  *)malloc( n *  sizeof ( char  ));  //分配求编码的工作空间
       cd[  n -1]= '\0'    //编码结束符
         int  p,start,c;
         for ( int  i=1;i<= n ;i++)  //逐个求赫夫曼编码
       {
              start=  n -1;     //编码结束符位置
              c=i;
              p=  HT [i].parent;    //从叶子到根逆向求编码
                while (p)
              {
                       if ( HT  [p].lchild==c)
                           cd[--start]=  '0' ;
                       else
                           cd[--start]=  '1' ;
                     c=p;
                     p=  HT [p].parent;
              }
                //编码是从start到n-2,共n-start-1个字符,第n-1个位置为‘\0’,总共需要n-start。
                HC [i]=( char  *)malloc( sizeof (  char )*( n  -start));
                //strcpy(char* dest, const char *src);把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
              strcpy(  HC [i],&cd[start]);
       }
       free(cd);
}

//---从根出发遍历整棵树,求各个叶子结点所表示的字符------//
//--无栈飞递归遍历霍夫曼树,求霍夫曼编码!!!!!!!!!!!挺巧妙的算法!!
void  rootstartHuffmancode( HuffmanTree  & HT ,  HuffmanCode  & HC  , int  n )
{
         char  *cd=( char  *)malloc( n *  sizeof ( char  ));  //分配求编码的工作空间
         HC =( HuffmanCode  )malloc(( n +1)*  sizeof ( char  *)); //分配n个字符编码的头指针向量
         int  m=2* n  -1;   int  l,i,j=0;
         for (i=1;i<=m;i++)
                HT [i].weight=0;  //遍历赫夫曼树时用作结点的标志
       
       l=m;
         while (l)
       {
                if ( HT  [l].weight==0) //向左
              {
                       HT [l].weight=1;
                       if ( HT  [l].lchild== NULL  &&  HT [l].rchild== NULL  ) //登记叶子结点的编码
                     {
                             HC [l]=( char  *)malloc((j+1)* sizeof (  char ));        
                           cd[j]=  '\0' ;
                           strcpy(  HC [l],cd);                        
                     }
                       else
                     {
                           l=  HT [l].lchild; cd[j++]= '0'  ;
                     }
              }
                else  if  ( HT [l].weight==1)  //向右
              {
                       HT [l].weight=2;
                       if ( HT  [l].rchild!=0)
                     {
                           l=  HT [l].rchild; cd[j++]= '1'  ;
                     }
              }             
                else  //左右都走过了,返回
              {
                       HT [l].weight=0;      l= HT [l].parent;      --j;  //退回父亲结点,编码长度减1
              }
       }
       free(cd);
}
void   Select( HuffmanTree  HT ,  int  i  , int  &  s1 , int  & s2 )
//在HT[1..i]选择parent为0且weight最小的两个结点,序号为s1和s2。
{
         s1 = s2  =0;
         for ( int  j=1;j<= i ;j++)
       {
                if ( HT  [j].parent== NULL )
              {
                       if ( HT  [ s1 ].weight>  HT [j].weight)
                     {
                             int  temp= s1  ; //s1总比s2小,如果这里不将s1赋值给s2会出错。
                             s1 =j;
                             s2 =temp;
                     }
                       else  if  ( HT [  s2 ].weight> HT  [j].weight)
                             s2 =j;
              }
       }
}

main.cpp:
         //-------------Huffman TEST-------------------//
         int  w[100]={5,29,7,8,14,23,3,11}; //测试用例P148
         HuffmanTree  HT;
         HuffmanCode  HC;
       Huffmancoding(HT,HC,w,8);
         for ( int  i=1;i<=8;i++)
              printf(  "%s\n" ,*(HC+i));

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值