现在的位置: 首页 > 综合 > 正文

数据结构-练习7 huffman编码

2013年09月08日 ⁄ 综合 ⁄ 共 5462字 ⁄ 字号 评论关闭

        huffman编码是重要的编码方式之一。作为一名程序员,应该要做到融会贯通,因此我在网上搜些资料看,发现几乎没有适合我等菜鸟级的文章,不是没有图解,就是不说明为什么要引入huffman编码,稀里糊涂,于是自己整理了一下,欢迎诸君拍砖。

           

        

        Huffman是一种无损压缩方法:所谓无损就是不会有信息丢失,可以100%恢复原信号,像奈奎斯特恢复就是有损了;所谓压缩,就是可以占有更少的存储空间。

       比如要发送三个字符:S1,S2,S3。我们可以这样编码:00,01,10。当然这是小数据,如果要发送的图片或是视频呢?比如一副高清图片,10M,在局域网上即使可以达到理论的10Mbps,也要1s,没法满足视频的最低帧频24fps。因此压缩非常重要,当然采用的压缩很多,huffman只是早期的一种而已,这就是Huffman的背景。

      现在来看看它的原理:先不联系最优二叉树那套东西。基本原理是根据字符出现的概率确定其码子。即高概率的少码字,低概率的多码字。     举个例子吧:

                         

                                                                                                             

                                                                                                                                                    图 1

         结合上图:

                        A B C D E 5个字符及其出现的概率。过程很简单:

 

                        First:          从上到下,根据概率由大到小排列;

 

                        Second:    俩俩合并,先合并概率低的,比如D E 概率最低,合并之,得到概率0.1,再寻找概率最低的俩部分,发现是C 和 DE,合并之,合并后,

                       标出新的 概率值 ,如图中红色圈起来的部分,直到合并完全;

 

                       Third:         标出0或1。如图中蓝色部分,低概率的给0(1也可以),概率相等的,就0和1随意了;

 

                       Last:            得到编码值:A:1,B:10;C:100; D:1000; E:0000。

 

 

          整个编码过程完毕。以上就是Huffman编码的大体过程。好了,感受完其最基本的过程。我们再来完成最优二叉树(赫夫曼树)的代码编写,分为俩部分:第一部分为基本概念,第二部分为:代码的实现,并有测试结果。

         

          huffman 树:带权路径最短的树。所谓带权路径,就是从该节点到树根的长度与节点上的权的乘积                                                                                                                                                    

 

                                                                                                                                                图 2

 

 

 

          那么树的带权路径就是所有节点的带权路径之和,公式表示:         

如上图:套公式:6*1+4*2+1*3+2*3

        有了一开始部分的介绍,huffman二叉树的构造就很简单了,步骤:

          1,找到权重最小的俩节点,合并最为一个新的节点,比如上面D,E合并,得到一个新的节点,暂且叫  做DE,则D,E作为子节点,DE作为父节点;

           2,以DE代替D和E,DE的权重为D和E之和,重新第一步,直到所有节点都加入到二叉树中。

           3,对于权重小的节点附0(或1),对于权重大的节点附1(或0)。

           如把图1huffman的计算过程变成二叉树的 图形如下:

                                                                                                                       

                                                                                                                                                             图 3

      读者,把一开始的图1 ,旋转过来跟图 2 是一样的,因为原理一样。注意:huffman叶节点为n,需要2*n-1个节点来构成二叉树,树的节点的度为2(除去叶节点)。

      第二部分:以一个例子开始:  权重W={5,29,7,8,14,23,3,11}.n=8.此例子为严蔚敏第159页的例子。

               上代码:                                                                                                                                                                                                        

/*
Coder  Information
      e_mail:shenganbeiyang@163.com
      QQ:501968942
*/

#ifndef HUFF_H
#define HUFF_H
#include<string>
//定义一个结构体,包括节点的父节点和左右子节点

//字符的个数
const int maxnum=30;
typedef struct Tnode
{
 char val;
 int weight;
 int parent;
 int lchild;
 int rchild;

}huffmanNode ,*huffmanTree;

class huffmanCoding
{
public:
	//初始化最优二叉树(哈夫曼树)
	huffmanCoding(huffmanNode * hn,int length);

    //编码过程
    void  Selection(int);
	void  Encode(int);
    
	//解码过程
//    string	deCode();

	//deconstruction function
	~huffmanCoding(){
		for(int i=0;i<length;++i)
		delete HC[i];
		delete [] HC;
	};
	 char** HC;//保存编码信息

private:

	huffmanNode InitData[maxnum];
	//char** HC;//保存编码信息
	int length;


};
//constructor function.初始化二叉树
inline huffmanCoding::huffmanCoding(huffmanNode * hn,int length_)
{
   HC=new char*[length_];//(sizeof(char*)*length);//new (char*)[length];
   length=length_;
   int i;
   for( i=0;i<length;++i)
   {
    InitData[i]=hn[i];
	InitData[i].parent=-1;
	InitData[i].rchild=-1;
	InitData[i].lchild=-1;  
   }
   for(int j=i;j<2*length;++j)
   {
	InitData[j].lchild=-1;
	InitData[j].rchild=-1;
	InitData[j].weight=-1;
	InitData[j].val='0';
	InitData[j].parent=-1;
   
   }

}
//编码huffman二叉树
inline void huffmanCoding::Selection(int codedlength)
{
	int pos_1,pos_2;
	pos_1=pos_2=-1;
	int i;
    for( i=0;i<codedlength;++i)
	{
		if(InitData[i].parent==-1)
		{
			pos_1=i;
		    break; 
		}
	}
    for(int k=pos_1+1;k<codedlength;++k)
	{
		if(InitData[k].weight<InitData[pos_1].weight&&InitData[k].parent==-1)
		{
		 pos_1=k;
		}
	
	}
	    for(int j=0;j<codedlength;++j)
	    {
		if(InitData[j].parent==-1&&j!=pos_1)
		{
		pos_2=j;
	    break;
		}

	    }
     for(int j=pos_2+1;j<codedlength;++j)
	 {  
		 if(InitData[j].weight<InitData[pos_2].weight&&InitData[j].parent==-1&&j!=pos_1)
		 {  
		 pos_2=j;
		 
		 }
	      
	 }
	 if(InitData[pos_1].weight<InitData[pos_2].weight)
	 {
		 InitData[codedlength].lchild=pos_1;
		 InitData[codedlength].rchild=pos_2;
		 InitData[pos_1].parent=codedlength;
		 InitData[pos_2].parent=codedlength;
		 InitData[codedlength].weight=InitData[pos_1].weight+InitData[pos_2].weight;

	 }
	 else
	 {
	    
		 
	     InitData[codedlength].lchild=pos_2;
		 InitData[codedlength].rchild=pos_1;
		 InitData[pos_1].parent=codedlength;
		 InitData[pos_2].parent=codedlength;
		 InitData[codedlength].weight=InitData[pos_1].weight+InitData[pos_2].weight;
		 
	 }
     	
}
inline void huffmanCoding::Encode(int len)
{
         for(int i=len;i<2*len-1;++i)
		 {
	     Selection(i);
		 }
         char*dc=new char[length];
	     for(int m=0;m<length;++m)
	     dc[m]='\0';
         
		 int start=0;
		 int tmp;
		 int p;
		 for(int m=0;m<length;++m)
		 {
			 for(tmp=m,p=InitData[m].parent;p!=-1;tmp=p,p=InitData[p].parent)
			 {
				 if(InitData[p].lchild==tmp)
					 dc[start++]='0';
				 else
					 dc[start++]='1';
			     
			 }
		  dc[start]='\0';
		  HC[m]=new char[length];
		  start=0;
          strcpy(HC[m],dc);
		 }
		delete [] dc;



}

#endif

测试代码:

#include"huff.h"
#include<iostream>
using namespace std;

int main()
{
 
    int i=0;
	huffmanTree ht=new huffmanNode[8];
	for(;i<8;++i)
	{
		
	    ht[i].val='a'+i;
	}
	ht[0].weight=5;
	ht[1].weight=29;
	ht[2].weight=7;
	ht[3].weight=8;
	ht[4].weight=14;
	ht[5].weight=23;
	ht[6].weight=3;
	ht[7].weight=11;



	huffmanCoding hc(ht,8);
	hc.Encode(8);
	for(int i=0;i<8;++i)
	cout<<hc.HC[i]<<endl;

	return 0;

}

测试结果,即编码结果为:

  

大家可能发现 与书上的结果不一样,严的书是 左节点必须为叶子节点,并且强制附0,我认为这不正确。huffman的定义不是这样的,如有异议可交流。

 

        

                        

                            

 

                         

 

抱歉!评论已关闭.