1. 程式人生 > >哈夫曼編碼

哈夫曼編碼

http sdn chm cstring htc 位數 child 個數 ostream

在電文傳輸中,需要將電文中出現的每個字符進行二進制編碼。在設計編碼時需要遵守兩個原則: (1)發送方傳輸的二進制編碼,到接收方解碼後必須具有唯一性,即解碼結果與發送方發送的電文完全一樣; (2)發送的二進制編碼盡可能地短。下面我們介紹兩種編碼的方式。 1. 等長編碼 這種編碼方式的特點是每個字符的編碼長度相同(編碼長度就是每個編碼所含的二進制位數)。假設字符集只含有4個字符A,B,C,D,用二進制兩位表示的編碼分別為00,01,10,11。若現在有一段電文為:ABACCDA,則應發送二進制序列:00010010101100,總長度為14位。當接收方接收到這段電文後,將按兩位一段進行譯碼。這種編碼的特點是譯碼簡單且具有唯一性,但編碼長度並不是最短的。
2. 不等長編碼 在傳送電文時,為了使其二進制位數盡可能地少,可以將每個字符的編碼設計為不等長的,使用頻度較高的字符分配一個相對比較短的編碼,使用頻度較低的字符分配一個比較長的編碼。例如,可以為A,B,C,D四個字符分別分配0,00,1,01,並可將上述電文用二進制序列:000011010發送,其長度只有9個二進制位,但隨之帶來了一個問題,接收方接到這段電文後無法進行譯碼,因為無法斷定前面4個0是4個A,1個B、2個A,還是2個B,即譯碼不唯一,因此這種編碼方法不可使用。 因此,為了設計長短不等的編碼,以便減少電文的總長,還必須考慮編碼的唯一性,即在建立不等長編碼時必須使任何一個字符的編碼都不是另一個字符的前綴,這宗編碼稱為前綴編碼(prefix code)
(1)利用字符集中每個字符的使用頻率作為權值構造一個哈夫曼樹; (2)從根結點開始,為到每個葉子結點路徑上的左分支賦予0,右分支賦予1,並從根到葉子方向形成該葉子結點的編碼 例題: 假設一個文本文件TFile中只包含7個字符{A,B,C,D,E,F,G},這7個字符在文本中出現的次數為{5,24,7,17,34,5,13} 利用哈夫曼樹可以為文件TFile構造出符合前綴編碼要求的不等長編碼 具體做法: 1. 將TFile中7個字符都作為葉子結點,每個字符出現次數作為該葉子結點的權值 2. 規定哈夫曼樹中所有左分支表示字符0,所有右分支表示字符1,將依次從根結點到每個葉子結點所經過的分支的二進制位的序列作為該 結點對應的字符編碼 3. 由於從根結點到任何一個葉子結點都不可能經過其他葉子,這種編碼一定是前綴編碼,哈夫曼樹的帶權路徑長度正好是文件TFile編碼 的總長度 通過哈夫曼樹來構造的編碼稱為哈弗曼編碼(huffman code)
技術分享
#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
  
#define N 10         // 帶編碼字符的個數,即樹中葉結點的最大個數  
#define M (2*N-1)    // 樹中總的結點數目  
  
class HTNode{        // 樹中結點的結構  
public:   
    unsigned int weight;  
    unsigned int parent,lchild,rchild;  
};                      
  
class HTCode{  
public:  
    char data;      // 待編碼的字符  
    int weight;     // 字符的權值  
    char code[N];   // 字符的編碼  
};  
  
void Init(HTCode hc[], int *n){  
// 初始化,讀入待編碼字符的個數n,從鍵盤輸入n個字符和n個權值  
    int i;  
    printf("input n = ");  
    scanf("%d",&(*n));  
  
    printf("\ninput %d character\n",*n);  
      
    fflush(stdin);  
    for(i=1; i<=*n; ++i)  
        scanf("%c",&hc[i].data);  
  
    printf("\ninput %d weight\n",*n);  
      
    for(i=1; i<=*n; ++i)  
        scanf("%d",&(hc[i].weight) );  
    fflush(stdin);  
}//  
  
void Select(HTNode ht[], int k, int *s1, int *s2){  
// ht[1...k]中選擇parent為0,並且weight最小的兩個結點,其序號由指針變量s1,s2指示  
    int i;  
    for(i=1; i<=k && ht[i].parent != 0; ++i){   
        ; ;  
    }  
    *s1 = i;  
  
    for(i=1; i<=k; ++i){  
        if(ht[i].parent==0 && ht[i].weight<ht[*s1].weight)  
            *s1 = i;  
    }  
  
    for(i=1; i<=k; ++i){  
        if(ht[i].parent==0 && i!=*s1)  
            break;  
    }  
    *s2 = i;  
  
    for(i=1; i<=k; ++i){  
        if(ht[i].parent==0 && i!=*s1 && ht[i].weight<ht[*s2].weight)  
            *s2 = i;  
    }  
}  
  
void HuffmanCoding(HTNode ht[],HTCode hc[],int n){  
// 構造Huffman樹ht,並求出n個字符的編碼  
    char cd[N];  
    int i,j,m,c,f,s1,s2,start;  
    m = 2*n-1;  
      
    for(i=1; i<=m; ++i){  
        if(i <= n)  
            ht[i].weight = hc[i].weight;  
        else  
            ht[i].parent = 0;  
        ht[i].parent = ht[i].lchild = ht[i].rchild = 0;  
    }  
  
    for(i=n+1; i<=m; ++i){  
        Select(ht, i-1, &s1, &s2);  
        ht[s1].parent = i;  
        ht[s2].parent = i;  
        ht[i].lchild = s1;  
        ht[i].rchild = s2;  
        ht[i].weight = ht[s1].weight+ht[s2].weight;  
    }  
  
    cd[n-1] = \0;  
  
    for(i=1; i<=n; ++i){  
        start = n-1;  
        for(c=i,f=ht[i].parent; f; c=f,f=ht[f].parent){  
            if(ht[f].lchild == c)  
                cd[--start] = 0;  
            else  
                cd[--start] = 1;  
        }  
        strcpy(hc[i].code, &cd[start]);  
    }  
}  
  
  
int main()  
{  
    int i,m,n,w[N+1];  
    HTNode ht[M+1];  
    HTCode hc[N+1];  
    Init(hc, &n);     // 初始化  
    HuffmanCoding(ht,hc,n);   // 構造Huffman樹,並形成字符的編碼  
  
    for(i=1; i<=n; ++i)    
        printf("\n%c---%s",hc[i].data,hc[i].code);    
    printf("\n");  
  
    return 0;  
}  

哈夫曼編碼