1. 程式人生 > >赫夫曼樹和赫夫曼樹編碼

赫夫曼樹和赫夫曼樹編碼

赫夫曼樹,又稱最優樹,是一類帶權路徑長度最短樹。

從樹中一個節點到另一個節點之間的分支構成這兩個節點之間的路徑,路徑上的分支數目稱為路徑長度。樹的路徑長度指的是從樹根到樹中其他每個節點的路徑長度之和。節點的帶權路徑長度是指從樹的根節點到該節點之間的路徑長度與該節點上所帶權值的乘積。樹的帶權路徑長度定義為樹中所有葉子節點的帶權路徑長度之和。

 1、構造赫夫曼樹

(1)根據給定的n個權值{ w1, w2, w3, .........,wn},構成 n 棵二叉樹的集合,其中每顆二叉樹 Ti 中只有一個帶權為 wi 的根節點,其左右子樹均為空。

(2)在 F 中選取兩棵根節點的權值最小的樹作為左右子樹,構造一顆新的二叉樹,且置新的根節點的權值為其左右子樹根節點的權值之和。

(3)在 F 中刪除這兩棵樹,同時將新得到的二叉樹加入到 F 中。

重複(2)(3),直到 F 中只剩一棵樹為止,這棵樹便是所求的赫夫曼樹。

注: 由於赫夫曼樹沒有度為 1 的結點,則一棵含有 n 個葉子節點的赫夫曼樹共有 2n-1 個節點,可以儲存在大小為 2n-1 的一維數組裡面。

構造赫夫曼樹的程式碼如下:

typedef struct
{
   int weight;
   int lchild,rchild;
}HTNode;
typedef struct
{
   HTNode *HTree;//動態分配陣列儲存樹的結點
   int root;//根節點的位置
}HuffmanTree;

void CreateHuffmanTree(HuffmanTree &HT, int *w, int n)
{//w存放 n 個權值(均大於0),構造赫夫曼樹HT.
   if(n<=1) return 0;
   m=2*n-1;
   HT.HTree=new HTNode[m]; //為赫夫曼樹分配一組順序空間
   for(p=HT.HTree, i=0; i<n; ++i,++p,++w) *p={*w, -1, -1}//n 個帶權結點形成初始化的森林,每個左右孩子為空
   for( ; i<m; i++) *p={0, -1, -1};//對尚未使用的結點賦初值
   for(i=n; i<m; i++)
   {//構建赫夫曼樹
        Select(HT.HTree, i-1, s1, s2);//在 HT.HTree[1...i-1]中找到兩個權值最小的結點
                                      //其序號是 s1和 s2
        HT.HTree[i].lchild=s1; HT.HTree[i].rchild=s2;
       HT.HTree[i].weight=HT.HTree[s1].weight + HT.HTree[s2].weight;//取左右樹根節點權值之和
    }
   HT.root=m-1;
}

假設有五個權值為  2  ,3 , 6, 7 ,5的結點,則有

HT的初態
HT weight lchild rchild
0 2 -1 -1
1 3 -1 -1
2 6 -1 -1
3 7 -1 -1
4 5 -1 -1
5 0 -1 -1
6 0 -1 -1
7 0 -1 -1
8 0 -1 -1
最終得到的赫夫曼樹是:
HT的終態
HT weight lchild rchild
0 2 -1 -1
1 3 -1 -1
2 6 -1 -1
3 7 -1 -1
4 5 -1 -1
5 5 0 1
6 10 4 5
7 13 2 3
  8 23 7 6

2、赫夫曼樹編碼

從根節點出發,對赫夫曼樹進行先序遍歷,並在遍歷的過程中“以棧記下所經的路徑(向左記 0, 向右記 1)”,則從根到每個葉子節點的路徑即為各個對應字元的編碼。

typedef char **HuffmanCode;//動態分配陣列空間儲存赫夫曼樹編碼

void HuffmanCoding(HuffmanTree HT, HuffmanCode &HC, int n)
{//先序遍歷赫夫曼樹 HT,求樹上 n 個葉子節點的編碼存入 HC中
   Stack S;//附設棧儲存路徑
   HC=new (char *)[n];二維陣列儲存編碼
   InitStack(S);//初始化棧空間
   Coding(HT, HT.root, S);
}

void Coding(HuffmanTree T, int i, Stack &S)
{
  if(T)
  {
     if( (T.HTree[i].lchild==-1) && (T.HTree[i].rchild==-1))
     {//到達葉子節點
         HC[i]=new char[StackLength(S)];
         StackCopytoArray(S, HC[i]);//從棧底到棧頂將棧中的字元複製到 HC[i] 中
     }
     else
     {
         Push(S, '0');
         Coding(T, T.HTree[i].lchild, S);//遍歷左子樹
         Pop(S, e);//回溯一個節點
         Push(S, '1')
         Coding(T, T.HTree[i].rchild, S);//遍歷右子樹
         Pop(S, e);//回溯一個節點
     }
  }
}