Huffman编码的实现

      这里的Huffman编码就是离散数学中学的最优二叉树,现在我们用C++来实现它。

      首先考虑实现它使用的数据结构,当然这里肯定使用二叉树。其次就是存储方式,对于树一般情况是用间接存储也就是链表。但是二叉链表不方便寻找双亲节点,这里使用向量更好,对存储空间的利用率也较高。

  建立数据结构:

       Huffman树中每个节点看成数组中的一个元素,它的属性有:权(weight),字符(data) 。为了方便寻找双亲和孩子节点加入三个域parent ,lch ,rch。分别存储双亲和左右孩子节点在数组中位置下标。

       然后再建立个类来操作Huffman树。

// Huffman.h
#include  < string >
using  std:: string ;

class  HuffmanTree
{
public :
   HuffmanTree(
int , int []);
    
   
void  BuildTree();
   
void  SetCode();
   
void  Display();
   
string  Send( char * );
   
string  Get( string );
    
private :
   
void  Select( int , int & , int & );
    
private :    
    
class  Node
    {
         friend 
class  HuffmanTree;
         
int  weight;
         
char  data;
         
string  code;
         
int  parent,lchild,rchild;
     }
* nodes;
      
int  codenum;
      
int  nodenum;
};

        这里,我在节点中又加了个code属性,这个是每个字符的最后编码。codenum是叶子节点数,也就是需要编码的字符节点数。nodenum是总节点数。要让外面那个类能操作Node类,则需要声明为它的友元函数。

        Huffman数建立的过程:

        1. 根据给定的n个权值构成n棵二叉树。

        2. 从所有的二叉树中选取两个权值最小节点的构成一个新的树,这个树的权值是两个节点权值之和。

        3. 重复2,直到只剩一棵树为止。

 下面是实现这个类的代码:

// Huffman.cpp
#include  " Huffman.h "
#include 
< iostream >
#include 
< string >
using   namespace  std;

HuffmanTree::HuffmanTree(
int  num, int  w[]):codenum(num)
{
      nodenum
= 2 * codenum - 1 ;
      nodes
= new  Node[nodenum];
      
for ( int  i = 0 ;i < codenum;i ++ )
      {
            nodes[i].weight
= w[i];
            nodes[i].data
= ' a ' + i;
            nodes[i].parent
= nodes[i].lchild = nodes[i].rchild =- 1 ;
            nodes[i].code
= "" ;
      }
      
for ( int  i = codenum;i < nodenum;i ++ )
      {
            nodes[i].weight
= 0 ;
            nodes[i].data
= 0 ;
            nodes[i].parent
= nodes[i].lchild = nodes[i].rchild =- 1 ;
     }
}

void  HuffmanTree::BuildTree()
{
       
for ( int  i = codenum;i < nodenum;i ++ )
      {
             
int  s1 = 0 ,s2 = 0 ;
             Select(i,s1,s2);
             nodes[s1].parent
= nodes[s2].parent = i;
             nodes[i].lchild
= s1;
             nodes[i].rchild
= s2;
             nodes[i].weight
= nodes[s1].weight + nodes[s2].weight;
       }        
}

void  HuffmanTree::Select( int  size, int &  s1, int &  s2)
{
      
int  temp = 0 ;    
      
for ( int  i = 0 ;i < size;i ++ )
      {
             
if (nodes[i].parent !=- 1 )
    
continue ;
             
if (temp == 0 )
             {
    s2
= i;
    temp
++ ;
    
continue ;
             }
             
if (temp == 1 )
             {
    
if (nodes[i].weight > nodes[s2].weight)
    {
            s1
= s2;
            s2
= i;
    }
    
else
            s1
= i;
    temp
++ ;
    
continue ;
             }
             
if (nodes[i].weight < nodes[s2].weight)
             {
    
if (nodes[i].weight < nodes[s1].weight)
    {
            s2
= s1;
            s1
= i;
    }                
    
else
            s2
= i;
             }
      }
}

void  HuffmanTree::SetCode()
{
        
for ( int  i = 0 ;i < codenum;i ++ )
        {
                
int  j = i;
                
int  p = nodes[j].parent;
                
while (p !=- 1 )
                {
       
if (nodes[p].lchild == j)
                 nodes[i].code
= " 0 " + nodes[i].code;
      
else
                nodes[i].code
= " 1 " + nodes[i].code;
       j
= p;
       p
= nodes[j].parent;
                 }    
        }
}

void  HuffmanTree::Display()
{
       
for ( int  i = 0 ;i < codenum;i ++ )    
            cout
<< nodes[i].data << " :   weight =  " << nodes[i].weight << " : " << nodes[i].code << endl;
}

string  HuffmanTree::Send( char *  old)
{
        
string  newcode = "" ;
        
for (; * old;old ++ )
       {
              
for ( int  i = 0 ;i < codenum;i ++ )
             {
    
if (nodes[i].data ==* old)
    {
             newcode
+= nodes[i].code;
             
break ;
    }
             }
        }
        
return  newcode;
}

string  HuffmanTree::Get( string  old)
{
        
string  newcode = "" ;
        
string ::size_type index = 0 ;
        
int  treeindex = nodenum - 1 ;
        
while (index <= old.size())
        {
              
if (nodes[treeindex].lchild ==- 1 )
              {
    newcode
+= nodes[treeindex].data;
    treeindex
= nodenum - 1 ;
              }
              
else
             {
    
if (old[index] == ' 0 ' )
            treeindex
= nodes[treeindex].lchild;
    
else
            treeindex
= nodes[treeindex].rchild;
    index
++ ;
              }        
          }
         
return  newcode;
}

    构造函数:用待编码字符数和每个字符的权值来初始化。
                       根据节点个数在堆区建立数组。
                       节点分两种叶子节点(为编码的字符)和非叶子节点,所以分开初始化。

    Build 函数:这个是根据Huffman树规则建立二叉树。

    Select 函数:从节点数组0到size的范围内选取两个权值最小的节点,下标放在s1,s2中。

    SetCode 函数:求出每个字符的编码。

    Display 函数:显示编码。

    Send 函数:给定一个字符串,求出它的编码。

    Get 函数:从得到的0、1串中翻译出它原编码。

    编码的过程就是从树叶到树根的路径,而反编码则是从树根到树叶的路径。

最后给出一个测试代码: 

// The test file
#include  < iostream >
#include 
< string >
#include 
" Huffman.h "

using   namespace  std;

int  main()
{
      
int  w[] = { 5 , 29 , 7 , 8 , 14 , 23 , 3 , 11 , 12 , 9 , 20 , 17 , 13 , 57 , 32 };
      HuffmanTree
*  t = new  HuffmanTree( 15 ,w);
       t
-> BuildTree();
       t
-> SetCode();
       t
-> Display();
       
char  s[] = " aefbcgbam " ;
       
string  p = t -> Send(s);
       cout
<< p << endl;
       cout
<< t -> Get(p) << endl;
}

        最后说一点,操作字符串在C++中,使用标准库中的string类比自己来拨弄指针方便多了,大家以后多试着用下,可以发现它的很多好处。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值