數據結構-樹-哈夫曼編碼

前言

哈夫曼編碼(Huffman Coding),又稱霍夫曼編碼,是一種編碼方式。該方法徹底依據字符出現機率來構造異字頭的平均長度最短的碼字,有時稱之爲最佳編碼,通常就叫作Huffman編碼(有時也稱爲霍夫曼編碼)。數組

哈夫曼思考

下圖是學生成績分佈圖: markdown

若是轉換成代碼一般是:

if( sum < 60) 
 result = 「不及格」; 
else if(sum < 70) 
 result = 「及格」; 
else if(sum < 80) 
 result = 「中等」; 
else if(sum < 90) 
 result = 「良好」; 
else 
 result = 「優秀」; 
複製代碼

若是是數量集很大的狀況下,這樣的比較會出現很嚴重的效率問題。由於人數比重比較多的是70-79分,想要獲得「中等」這個結果,須要通過3次判斷。其實根據所佔比例(權重),能夠生成一個更優質的二叉樹結構。

一般咱們使用WPL(樹的帶權路徑長度)來計算優質性,權重值就是學生所佔比例 x 樹到指定節點的路徑優化

  • 優化前:1x5 + 2x15 + 3x40 + 4x30 + 4x10 = 315
  • 優化後:5x3 + 15x3 + 40x2 + 30x2 + 10x2 = 220

構建過程

咱們使用傳輸文字內容爲例: 編碼

內容ABCDEF分別用000、00一、0十、0十一、100、101表示。傳輸BADCADFEED這個內容。

  1. 排序
  2. 構建樹
  3. 左孩子的路徑用0表示,右孩子用1表示
  4. 返回新編碼

代碼

#include <stdio.h>
#include "string.h"
#include "stdlib.h"

const int MaxValue = 100000;//初始化射電的權值最大值
const int MaxBit = 4;//初始最大編碼位數
const int MaxN = 10;//最大結點數

typedef struct HaffNode {
    int weight;//權重
    int flag;//是否加入到合併到樹中。0:爲合併; 1:合併
    int parent;//雙親結點下標
    int lChild;//左孩子下標
    int rChild;//右孩子下標
}HaffNode;

typedef struct HaffCode{
    int bit[MaxBit];//編碼數組
    int length;//數組長度
    int weight;//權重
}HaffCode;

/** 構建哈弗曼樹 */
void haffman(int weight[], int n, HaffNode *haffTree) {
    //哈夫曼樹的結點度爲0或者2,因此n個葉子結點,總結點數爲2n-1
    for (int i = 0; i < 2*n-1; i++) {
        if (i < n) {
            haffTree[i].weight = weight[i];
        } else {
            haffTree[i].weight = 0;
        }
        haffTree[i].parent = 0;
        haffTree[i].flag = 0;
        haffTree[i].lChild = -1;
        haffTree[i].rChild = -1;
    }
    
    int value1, value2;
    int index1, index2;
    
    //構造哈夫曼樹
    for (int i = 0; i < n-1; i++) {
        value1 = value2 = MaxValue;
        index1 = index2 = 0;
        
        //循環找出權重最小的兩個值
        int j;
        for (j = 0; j < n+i; j++) {
            if (haffTree[j].weight < value1 && haffTree[j].flag == 0) {
                value2 = value1;
                index2 = index1;
                value1 = haffTree[j].weight;
                index1 = j;
            } else if (haffTree[j].weight < value2 && haffTree[j].flag == 0) {
                value2 = haffTree[j].weight;
                index2 = j;
            }
        }
        
        //將兩個最小值合併成一個子樹
        haffTree[index1].parent = n+i;
        haffTree[index2].parent = n+i;
        
        haffTree[index1].flag = 1;
        haffTree[index2].flag = 1;
        
        haffTree[n+i].weight = haffTree[index1].weight + haffTree[index2].weight;
        
        haffTree[n+i].lChild = index1;
        haffTree[n+i].rChild = index2;
    }
}

/** 根據哈夫曼樹建立哈夫曼編碼 */
void haffmanCode(HaffNode haffTree[], int n, HaffCode haffCode[]) {
    //建立一個編碼結點
    HaffCode *code = (HaffCode*)malloc(sizeof(HaffCode));
    int child, parent;
    //遍歷到權重數組長度
    for (int i = 0; i< n; i++) {
        code->length = 0;
        code->weight = haffTree[i].weight;
        child = i;
        parent = haffTree[i].parent;
        while (parent != 0) {
            if (haffTree[parent].lChild == child) {
                code->bit[code->length] = 0;//左孩子結點編碼0
            } else {
                code->bit[code->length] = 1;//右孩子結點編碼1
            }
            code->length ++;
            child = parent;
            parent = haffTree[child].parent;
        }
        
        //編碼是從子結點到父節點的順序生成的,是反的,因此要反向取編碼
        int temp = 0;
        for (int j = code->length - 1; j>=0; j--) {
            temp = code->length - j - 1;
            haffCode[i].bit[temp] = code->bit[j];
        }
        
        haffCode[i].length = code->length;
        haffCode[i].weight = code->weight;
    }
}
複製代碼

運行

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, 哈夫曼編碼!\n");
    int i, j, n = 4, m = 0;
    
    //權值
    int weight[] = {2,4,5,7};
    
    //初始化哈夫曼樹, 哈夫曼編碼
    HaffNode *myHaffTree = malloc(sizeof(HaffNode)*2*n-1);
    HaffCode *myHaffCode = malloc(sizeof(HaffCode)*n);
    
    //當前n > MaxN,表示超界. 沒法處理.
    if (n>MaxN)
    {
        printf("定義的n越界,修改MaxN!");
        exit(0);
    }
    
    //1. 構建哈夫曼樹
    haffman(weight, n, myHaffTree);
    //2.根據哈夫曼樹獲得哈夫曼編碼
    haffmanCode(myHaffTree, n, myHaffCode);
    //3.
    for (i = 0; i<n; i++)
    {
        printf("Weight = %d\n",myHaffCode[i].weight);
        for (j = 0; j<myHaffCode[i].length; j++)
            printf("%d",myHaffCode[i].bit[j]);
        m = m + myHaffCode[i].weight*myHaffCode[i].length;
         printf("\n");
    }
    printf("Huffman's WPL is:%d\n",m);
    return 0;
}
複製代碼

相關文章
相關標籤/搜索