哈夫曼树及其应用

哈夫曼编码是广泛用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%90%之间。哈夫曼编码算法使用字符在文件中出现的频率表来建立一个用01串表示各字符的最优表示方式。

     给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长。

1.前缀码

  对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。这种编码称为前缀码

编码的前缀性质可以使译码方法非常简单。

2.构造哈夫曼编码

  哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼算法

  哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T

  算法以|C|个叶结点开始,执行|C|1次的合并运算后产生最终所要求的树T

3.算法步骤:以二叉树为例)

根据给定的 n 个权值 {w1, w2, …, wn},构造 n 棵二叉树的集合F = {T1,   T2, … , Tn},其中每棵二叉树中均只含一个带权值为 wi的根结点, 其左、右子树为空树;

F 中选取其根结点的权值为最小的两棵二叉树,分别作为左、右子树构造一棵新的二叉树,并置这棵新的二叉树根结点的权值为其左、右子树根结点的权值之 和

F中删去这两棵树,同时加入刚生成的新树;

重复 (2) (3) 两步,直至 F 中只含一棵树为止。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

#define N 30	//待编码字符的个数,即树中叶结点的最大个数
#define M 2*N-1//树中总的结点数目

typedef  struct //树中结点的结构 
{
   int weight;
   int parent;
   int LChild;
   int RChild;
}HTNode,HuffmanTree[M+1];//0号单元不使用 

typedef char* Huffmancode[N+1];

void select(HuffmanTree HT, int g, int *s1, int *s2);
//构造Huffman树ht
void  CrtHuffmanTree(HuffmanTree ht,int w[],int n)
{
	int i,s1,s2,m;
	m=2*n-1;
	
	if(n<1)  
       return ;
	//初始化前 n 个元素成为根节点 
	for(i=1;i<=n;i++){
		ht[i].LChild=0;
		ht[i].parent=0;
		ht[i].RChild=0;
		ht[i].weight=w[i];	
	}      
	//初始化后n-1个为空元素 
	for(i=n+1;i<=m;i++)
	{
		ht[i].LChild=0;
		ht[i].parent=0;
		ht[i].RChild=0;
		ht[i].weight=0;
	}
	//从第n+1个元素开始构造新结点 
	for(i=n+1;i<=m;i++){
		select(ht,i-1,&s1,&s2);
		(ht+i)->weight=ht[s1].weight+ht[s2].weight;
		ht[s1].parent=i;
		ht[s2].parent=i;
		ht[i].LChild=s1;
		ht[i].RChild=s2;
	}
}
void select(HuffmanTree HT, int g, int *s1, int *s2)  
{  
 	int j, k, m, n;  
    for(k=1; k<=g; k++)     //找到一个parent为0的子树  
	{  
       if(HT[k].parent==0)  
       {  
         *s2=k;  
       	 break;  
        }  
    }  
    for(j=1; j<=g; j++)  
    {  
       if((HT[j].weight<=HT[k].weight)&&(HT[j].parent==0))  //找到一个parent为0权值最小的子树  
       *s2=j;  
    }  
    for(m=1; m<=g; m++)  
    {  
       if((HT[m].parent==0)&&(m!=*s2))  
       {  
        *s1=m;  
        break;  
        }  
    } 
	 
    for(n=1; n<=g; n++)  
    {  
      if((HT[n].weight<HT[m].weight)&&(HT[n].parent==0)&&(n!=*s2))  
        *s1=n;  
    }   
		
}  
void CrtHuffmanCode(HuffmanTree ht,Huffmancode hc,int n)
{   
     char *cd;
     int c;
     int i;
     int p;
     int start;
     cd=(char *)malloc((n+1)*sizeof(char));
     cd[n]='\0';
     for (i = 1; i <= n; i ++)
      {  
          start=n;
          c= i;		//c为当前节点,p为其双亲 
          p=ht[i].parent;
          while (p!=0)
          {  
              --start;
              if (ht[p].LChild==c)  cd[start]='0';
              else                	cd[start]='1';
              c=p;
              p=ht[p].parent; 
          }
              hc[i]= (char *)malloc((n-start+1)*sizeof(char));
              strcpy(hc[i],cd+start);
      }
      
      free(cd);
 }
 void CharSetHuffmanDecoding(HuffmanTree T, char* cd, int n)  
  {  
       int p = 2*n-1;      //从根结点开始  
       int i=0;  
       //当要解码的字符串没有结束时  
       while(cd[i]!='\0')  
       {  
            //当还没有到达哈夫曼树的叶子并且要解码的字符串没有结束时  
           while((T[p].LChild!=0 && T[p].RChild != 0) && cd[i] != '\0')  
           {  
                 if(cd[i] == '0')  
                 {  
                    //如果是0,则叶子在左子树  
                    p=T[p].LChild;  
                 }  
                 else  
                 {  
                    //如果是1,则叶子在左子树  
                    p=T[p].RChild;  
                 }  
                 i++;  
           }  
    	  //如果到达哈夫曼树的叶子时  
       		if(T[p].LChild == 0 && T[p].RChild == 0)  
       		{  
           		printf("%2d", T[p].weight);  
          		p = 2*n-1;  
       		 }  
       		else      //如果编号为p的结点不是叶子,那么编码有错  
        	{  
             	printf("\n解码出错! \n");  
           		return;  
        	 }  
       }  
       printf("\n");  
}  

int main(void){
	
	HuffmanTree ht;
	
	int i=1,temp,j;
	int w[N];
	printf("请输入叶子结点,输入0时结束:\n");
	 scanf("%d",&temp);  //读入叶子结点的权值  
     while(temp!=0) {
	    w[i++]=temp;
        scanf("%d",&temp);  
      } 

	CrtHuffmanTree(ht,w,i-1);
	
	for(j=1;j<=2*(i-1)-1;j++){
		printf("%d\n",ht[j].weight);
	}
	
	Huffmancode hc;
	
	CrtHuffmanCode( ht,hc,i-1);
	
	for(j=1;j<=i-1;j++){
		printf("%d的编码为:",ht[j].weight);
		puts(hc[j]);
	}
	
	char cd[100];
	printf("请输入待译码串:");
	scanf("%s",cd);
	getchar();
	puts(cd); 
	
	CharSetHuffmanDecoding(ht, cd, i-1);  
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值