赫夫曼(Huffman)樹/編碼

 

一 . 基本概念:

    赫夫曼樹:給定帶權的N個葉子構成的所有二叉樹中,樹的帶權路徑長度最小的二叉樹(最優二叉樹)

    帶權路徑長度:所有樹葉到樹根之間的路徑長度與該節點上權的乘積

    :賦予節點的有意義的參數

 

二 . 赫夫曼樹的構造

    例:設權值集合{2,4,5,7}

    1.根據權值進行排序,取最小的兩個葉子(2,4),較小的在左,較大的在右。添加一個新節點,節點的權值爲它倆的權值和,構成一顆二叉樹:

                                             

    2.用得到的節點作爲新的葉子,重複第一步:

                                               

    3.一直重複構造,直到所有子葉都取完

                                            

    所以共需要進行n-1次構造,總共有 n+n-1 = 2n -1 個節點;

    可以證明,這樣構造的二叉樹是帶權路徑長度最小的二叉樹

 

三 . 赫夫曼編碼

    1.數據壓縮(編碼):把文件中的每個字符轉換成一個唯一的二進制位串,且串裏面不能包含其它字符的表示串。

    2.赫夫曼編碼方法:如上例,設權爲字符在文檔中的重複次數,權值集合爲{A:2,B:4,C:5,D:7},從根節點開始向左是0,向右是1

                                                       

                那麼

                         A的編碼爲:110。

                         B的編碼爲:111。

                         C的編碼爲:10。

                         D的編碼爲:0。

    3.代碼實現:(C語言)

        頭文件等:

#include <stdio.h>
#include <stdlib.h>
#define MAXINT 32767;

typedef struct
{
  char Character;
  unsigned int Weight;//權重
  unsigned int Parent, Lchild, Rchild;//雙親節點,左子節點,右子節點
}HTNode;

 

        a.生成霍夫曼樹:

 

// n : 總葉子數  m: 用於構造Huffman樹的數組大小(2n-1)
//k, j : 循環變量
//C, W : 接收字符和權時的中轉變量
//Min_w1, Min_w2 : 用於存儲兩個最小權
//p1, p2 : 存儲兩個最小權數組的下標
void Great_Huffman(unsigned int n, HTNode HT[])
{
  int k , j;
  unsigned int W;
  char C;
  int m = 2*n - 1;
  //輸入n個字符及其權
  for(k = 0; k < n; k++)
  {
    printf("input char and Weight : ");
    while((C = getchar()) == '\n');
    scanf("%d", &W);
    HT[k].Character = C;
    HT[k].Weight = W;
    HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0;//初始化
  }
  //初始化之後用於連接子葉的n-1個節點
  for(k = n; k < m; k++)
  {
    HT[k].Character = '\0';
    HT[k].Weight = 0;
    HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0;
  }
  //構造赫夫曼樹
  int Min_w1, Min_w2;
  int p1, p2;
  //外層循環用於構造新的節點
  for(k = n; k < m; k++)
  {
    Min_w1 =  Min_w2 = MAXINT;//初始化最小值
    p1 = p2 = 0;//初始化下標
    //內層循環用於尋找構造新節點的兩個最小權節點
    for(j = 0; j < k; j++)
    {
      if(HT[j].Parent == 0)//如果這個子葉尚未合併
      {
        if(HT[j].Weight < Min_w1)//更新最小權
        {
          Min_w2 = Min_w1;
          p2 = p1;
          Min_w1 = HT[j].Weight;
          p1 = j;
        }
        else if(HT[j].Weight < Min_w2)//更新次小權
        {
          Min_w2 = HT[j].Weight;
          p2 = j;
        }
      }
    }
    HT[p1].Parent = HT[p2].Parent = k;
    HT[k].Lchild = p1; HT[k].Rchild = p2;
    HT[k].Weight = HT[p1].Weight + HT[p2].Weight;
  }
}

    (注意輸入格式,字符和權用空格隔開)

   

    b.生成霍夫曼編碼

//k : 循環變量
//sp : 編號指針,指向目前編號位,從後往前
//fp :雙親的下標
//p : 當前葉子下標
//HF : 暫時存儲當前子葉的赫夫曼編碼
void Huffman_Coding(unsigned int n, HTNode HT[])
{
  int k;
  int sp, fp;
  char *HF;//當前編碼
  HF = (char *)malloc(n*sizeof(char));
  int p;
  for(k = 0; k < n; k++)
  {
    sp = n-1; p = k; fp = HT[k].Parent;
    //從葉子搜索直到根節點
    while(fp != 0)
    {
      if(HT[fp].Lchild == p)//如果當前節點是左子樹
        HF[sp] = '0';
      else
        HF[sp] = '1';
      sp--;
      fp = HT[fp].Parent;
      p = fp;
    }
    //顯示編碼
    printf("\n%c : ", HT[k].Character);
    sp++;
    while(sp < n)
    {
      printf("%c",HF[sp]);
      sp++;
    }
  }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章