邊城愚人

          如果我不在邊城,我一定是在前往邊城的路上。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            31 隨筆 :: 0 文章 :: 96 評論 :: 0 Trackbacks

          ??? ??? 哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的路徑長度(若根結點為0層,葉結點到根結點的路徑長度為葉結點的層數)。樹的帶權路徑長度記為WPL=(W1*L1+W2*L2+W3*L3+...+ Wn*Ln)N個權值Wi(i=1,2,...n)構成一棵有N個葉結點的二叉樹,相應的葉結點的路徑長度為Li(i=1,2,...n)。可以證明哈夫曼樹的WPL是最小的。
          ??? ??? 構造哈夫曼樹的算法如下:
          ??? ??? 1
          )對給定的n個權值{W1,W2,W3,...,Wi,...,Wn}構成n棵二叉樹的初始集合F={T1,T2,T3,...,Ti,..., Tn},其中每棵二叉樹Ti中只有一個權值為Wi的根結點,它的左右子樹均為空。
          ??? ??? 2
          )在F中選取兩棵根結點權值最小的樹作為新構造的二叉樹的左右子樹,新二叉樹的根結點的權值為其左右子樹的根結點的權值之和。
          ??? ??? 3
          )從F中刪除這兩棵樹,并把這棵新的二叉樹同樣以升序排列加入到集合F中。
          ??? ??? 4
          )重復2)和3),直到集合F中只有一棵二叉樹為止。

          ??? ??? 例如,對于4個權值為1357的節點構造一棵哈夫曼樹,其構造過程如下圖所示(本人不善畫圖,使用DIA勉強畫出如此之圖):


          huf.jpeg


          ??? ??

          ??? ??? 可以計算得到該哈夫曼樹的路徑長度WPL(1+3)*3+2*5+1*7=26。

          ??? ??? 對于哈夫曼樹,有一個很重要的定理:對于具有n個葉子節點的哈夫曼樹,共有2*n-1個節點。

          ??? ??? 這個定理的解釋如下:對于二叉樹來說,有三種類型節點,即度數(只算出度)為2的節點,度數為1的節點和度數為0的葉節點。而哈夫曼樹的非葉子節點是由兩個節點生成的,因此不能出現度數為1的節點,而生成的非葉子節點的個數為葉子節點個數減一,于此定理就得證了。

          ??? ??? 這里給出構造哈夫曼樹的算法(算法實現使用C語言而不是java)。出于簡單性考慮,構造的哈夫曼樹不是采用鏈式存儲,而是以數組方式存儲,其中使用數組位置索引標識節點的鏈接。對于哈夫曼樹中的節點其數據類型如下:

          typedef?struct?QHTNode{
          ????char?c;??????
          //存儲的數據,為一個字符
          ????double?weight;?
          //節點權重
          ????
          int?parent;//父節點在數組中的位置索引
          ????
          int?lchild;//左孩子在數組中的位置索引
          ????
          int?rchild;//右孩子在數組中的位置索引
          }HTNode;

          ??? ??? 構造哈夫曼樹的算法的實現原理如下:對于n個葉子節點,我們根據上面的定理構造出大小為2*n-1的數組來存放整個哈夫曼樹。這個數組的前n個位置存放的為已知的葉子節點,后(n-1)個位置存放的為動態生成的樹內節點。在算法的大循環過程中,要做的事情就是根據位置i前面的已知節點(或者是葉節點或者是生成的樹內節點),找出 parent為-1(即節點尚且是一個子樹的根結點)的節點中權值最小的兩個節點,然后根據這兩個節點構造出位置為i的新的父節點(也就是一棵新樹的根結點)。程序如下:


          void?creatHuffmanTree(HTNode?ht[],int?n){
          ????
          int?i,j;
          ????
          int?lchild,rchild;
          ????double?minL
          ,minR;
          ????
          for(i=0;i<2*n-1;i++){
          ????????ht[i]
          .parent?=?ht[i].lchild?=?ht[i].rchild?=?-1;
          ????}
          ????
          for(i=n;i<2*n-1;i++){
          ????????minL?
          =?minR?=?MAXNUMBER;
          ????????lchild?
          =?rchild?=?-1;
          ????????
          for(j=0;j<i;j++){
          ????????????
          if(ht[j].parent?==?-1){
          ????????????????
          if(ht[j].weight?<?minL){
          ????????????????????minR?
          =?minL;
          ????????????????????minL?
          =?ht[j].weight;
          ????????????????????rchild?
          =?lchild;
          ????????????????????lchild?
          =?j;
          ????????????????}
          else?if(ht[j].weight?<?minR){
          ????????????????????minR?
          =?ht[j].weight;
          ????????????????????rchild?
          =?j;
          ????????????????}
          ????????????}
          ????????}
          ????????ht[lchild]
          .parent?=?ht[rchild].parent?=?i;
          ????????ht[i]
          .weight?=?minL?+?minR;
          ????????ht[i]
          .lchild?=?lchild;
          ????????ht[i]
          .rchild?=?rchild;
          ????}????
          }

          ??? ??? 哈夫曼樹的一個經典應用就是哈夫曼編碼。在數據通信中,經常需要將傳送的文字轉換成二進制字符串,這個過程就是編碼。哈夫曼編碼是一種變長的編碼方案,其核心就是使頻率越高的碼元(這個詞不知用的是否準確,就是要編碼的對象,可以是字符串等等了)采用越短的編碼。編碼過程就根據不同碼元的頻率(相當于權值)構造出哈夫曼樹,然后求葉子節點到根節點的路徑,其中節點的左孩子路徑標識為0,右孩子路徑標識為1。對于上面的例子,權值為1的節點編碼為000,權值為3的節點編碼為001,權值為5的節點編碼為01,權值為7的節點編碼為1

          ??? ??? 下面的實現采用的方法是從葉子節點向上遍歷到根結點,其中數據類型 HCode中的 code存儲路徑信息,而start表示路徑信息是從code數組的start位置開始的,結束位置為節點數n


          typedef?struct?QHCode{
          ????char
          *?code;
          ????
          int?start;
          }Hcode;

          void?createHuffmanCode(HTNode?ht[]
          ,HCode?hc[],int?n){
          ????
          int?i,f,c;
          ????HCode?father;
          ????
          for(i=0;i<n;i++){
          ????????hc[i]
          .start?=?n;
          ????????c?
          =?i;
          ????????
          while((f=ht[c].parent)?!=?-1){
          ????????????
          if(ht[f].lchild?==?c){
          ????????????????hc[i]
          .code[hc[i].start--]?=?'0';
          ????????????}
          else{
          ????????????????hc[i]
          .code[hc[i].start--]?=?'1';
          ????????????}
          ????????????c?
          =?f;
          ????????}
          ????????hc[i]
          .start++;
          ????}
          }

          ?

          ??? ??? 注:有關于數據結構及常用算法的系列文章的代碼將主要采用C語言,主要的原因是作者希望借此機會重新溫習一下C語言。數據結構及算法的學習重要的是思想,實現語言倒是其次。如果有人閱讀此代碼有困難,不妨在理解算法的基礎上使用擅長的語言(比如Java?)實現一下。該文參考了《數據結構習題與解析》一書。

          posted on 2007-06-21 08:23 kafka0102 閱讀(12250) 評論(7)  編輯  收藏 所屬分類: DS&Algorithms

          評論

          # re: 哈夫曼樹及哈夫曼編碼 2007-10-16 17:18 sd9218@hotmail.com
          請問一下
          如何把哈夫曼樹在匯編中實現...

          實現PC和FPGA開發板的字符哈夫曼編碼解碼   回復  更多評論
            

          # re: 哈夫曼樹及哈夫曼編碼[未登錄] 2008-10-09 12:34 呵呵
          作者的語言易懂!!  回復  更多評論
            

          # re: 哈夫曼樹及哈夫曼編碼 2009-04-29 16:56 文劍
          謝謝
          有了這篇文章,我更加不需要老師了  回復  更多評論
            

          # re: 哈夫曼樹及哈夫曼編碼 2010-08-26 18:54 輕帆向南
          "對于上面的例子,權值為1的節點編碼為000,權值為3的節點編碼為001,權值為5的節點編碼為01,權值為7的節點編碼為1……"
          根據上邊的圖,這個地方是不是算的不對?  回復  更多評論
            

          # re: 哈夫曼樹及哈夫曼編碼 2010-12-11 21:04 songshijia88888
          wpl沒算對吧,該是29。  回復  更多評論
            

          # re: 哈夫曼樹及哈夫曼編碼 2010-12-11 21:05 songshijia88888
          嗯。我看這里也有困惑。@輕帆向南
            回復  更多評論
            

          # re: 哈夫曼樹及哈夫曼編碼 2011-08-11 18:39 je
          應該是29  回復  更多評論
            

          主站蜘蛛池模板: 西城区| 瑞金市| 开化县| 剑河县| 铁岭市| 集贤县| 浮梁县| 邓州市| 禹城市| 汉沽区| 台北市| 陆河县| 罗田县| 武宁县| 金昌市| 大悟县| 玉田县| 松滋市| 焉耆| 华安县| 融水| 福建省| 琼海市| 满洲里市| 红安县| 瑞丽市| 石嘴山市| 泊头市| 长海县| 东海县| 永修县| 华安县| 漳浦县| 林州市| 公安县| 安达市| 嵊泗县| 鄯善县| 怀化市| 兰西县| 平谷区|