Huffman编码的实现

    技术2022-05-11  72

          这里的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类比自己来拨弄指针方便多了,大家以后多试着用下,可以发现它的很多好处。

     


    最新回复(0)