天天看點

資料結構——HuffmanTreeHuffman tree

Huffman tree

基本術語

  • 路徑和路徑長度
    • 路徑:在一棵樹中,從一個結點往下可以達到的孩子或子孫結點之間的通路。
    • 結點的路徑長度:從一個結點到另一個結點的路徑上分支的數目。
  • 結點的權及帶權路徑長度
    • 結點的權:将樹中結點賦予一個有着某種含義的數值。
    • 結點的帶權路徑長度:從根結點到該結點之間的路徑長度與該結點的權的乘積。
  • 樹的帶權路徑長度
    • 樹中所有葉子結點的帶權路徑長度之和。
  • 赫夫曼樹( Huffman tree )
    • 帶權路徑長度達到最小的二叉樹即為赫夫曼樹。
在所有含 n 個葉子結點、并帶相同權值的二叉樹中,必存在一棵其帶權路徑長度取最小值的樹,稱為“最優二叉樹”。

構造 Huffman tree

基本思想:使權大的結點靠近根
  • 根據給定的 n 個權值 {w1, w2, …, wn},構造 n 棵二叉樹的集合F = {T1, T2, … , Tn},其中每棵二叉樹中均隻含一個帶權值 為 wi 的根結點,其左、右子樹為空樹;
  • 在 F 中選取其根結點的權值為最小的兩棵二叉樹,分别作為左、

右子樹構造一棵新的二叉樹,并置這棵新的二叉樹根結點的權值為其左、右子樹根結點的權值之和;

  • 從F中删去這兩棵樹,同時加入

剛生成的新樹;

  • 重複上述兩步,直至 F 中隻含一棵樹為止。

哈夫曼構造算法實作

一棵有 n 個葉子結點的Huffman樹有 2n-1 個結點
  • 采用順序存儲結構---一維結構數組
  • 結點類型定義
    typedef struct {
        ElemType elem;  // 結點值
        int weight;  // 權值
        int parent, lch, rch;
    }HTNode, *HuffmanTree;           
  • 構造HuffmanTree
    • 輸入初始n個葉子結點:置HT[1..n]的weight值
    • 進行以下n-1次合并,依次産生HT[i],i=n+1..2n-1:
      • 在HT[1..i-1]中選兩個未被選過的weight最小的兩個結點HT[s1]和HT[s2] (從parent = 0 的結點中選)
      • 修改HT[s1]和HT[s2]的parent值: parent=i
      • 置HT[i]:weight=HT[s1].weight + HT[s2].weight ,lch=s1, rch=s2
Status CreatHuffmanTree(HuffmanTree HT, int n){
    if (n <= 1)  // 結點數量不合法
        return ERROR;
    int m = 2 * n - 1;
    int i;
    int s1, s2;
    HT = new HTNode[m + 1];  // 0号單元未用,HT[m]表示根結點   
    for (i = 1;i <= m;++i) {
        HT[i].lch = 0;
        HT[i].rch = 0;
        HT[i].parent = 0;
    }
    for (i = 1;i <= n;++i)
        // 輸入權值
        cin >> HT[i].weight;
    for (i = n + 1;i <= m;++i) {
        // 構造Huffman樹
        Select(HT, i - 1, &s1, &s2);  // 在HT[k](1≤k≤i-1)中選擇兩個其雙親域為0, 且權值最小的結點, 并傳回它們在HT中的序号s1和s2
        if (s1 != 0 && s2 != 0) {
            HT[s1].parent = i;
            HT[s2].parent = i;  //表示從F中删除s1,s2
            HT[i].lch = s1;
            HT[i].rch = s2;  //s1,s2分别作為i的左右孩子
            HT[i].weight = HT[s1].weight + HT[s2].weight;  //i 的權值為左右孩子權值之和
        }
    }
    return OK;
}           

哈夫曼樹的應用

哈夫曼編碼

  • 算法實作
    Status CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n) {
        if (n <= 1)
            return ERROR;
        int start, i;
        int f = 0, c;
        HC = new char* [n + 1];
        char* cd = new char[n];
        cd[n - 1] = '0';
        for (i = 1; i <= n; ++i) {
            while (f != 0) {
                // 從葉子結點開始向上回溯,直到根結點
                start = n - 1;
                c = i;
                f = HT[i].parent;
                if (HT[f].lch == c) cd[start] = '0';
                else cd[start] = '1';
                c = f;
                f = HT[f].parent;
            }
            HC[i] = new char[n - start];  // 編碼數組
            strcpy(HC[i], &cd[start]);
        }
        delete cd;
        cd = NULL;
        return OK;
    }           
  • 重要結論
    • 哈夫曼編碼是不等長編碼
    • 哈夫曼編碼是字首編碼,即任一字元的編碼都不是另一字元編碼的字首
    • 哈夫曼編碼樹中沒有度為1的結點。若葉子結點的個數為n,則哈夫曼編碼樹的結點總數為 2n-1
    • 發送過程:根據由哈夫曼樹得到的編碼表送出字元資料
    • 接收過程:按左0、右1的規定,從根結點走到一個葉結點,完成一個字元的譯碼。反複此過程,直到接收資料結束

繼續閱讀