哈弗曼樹與哈弗曼編碼簡介

樹和哈夫曼編碼。哈夫曼編碼是哈夫曼樹的一個應用。哈夫曼編碼應用廣泛,如JPEG中就應用了哈夫曼編碼。 首先介紹什麼是哈夫曼樹。哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的 路徑長度(若根結點爲0層,葉結點到根結點的路徑長度爲葉結點的層數)。樹的帶權路徑長度記爲

WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),N個權值Wi(i=1,2,...n)構成一棵有N個葉結點的二叉樹,相應的葉結點的路徑長度爲Li(i=1,2,...n)。可以證明哈夫曼樹的WPL是最小的。

哈夫曼編碼步驟:

一、對給定的n個權值{W1,W2,W3,...,Wi,...,Wn}構成n棵二叉樹的初始集合F= {T1,T2,T3,...,Ti,...,Tn},其中每棵二叉樹Ti中只有一個權值爲Wi的根結點,它的左右子樹均爲空。(爲方便在計算機上實現算法,一般還要求以Ti的權值Wi的升序排列。)
二、在F中選取兩棵根結點權值最小的樹作爲新構造的二叉樹的左右子樹,新二叉樹的根結點的權值爲其左右子樹的根結點的權值之和。
三、從F中刪除這兩棵樹,並把這棵新的二叉樹同樣以升序排列加入到集合F中。
四、重複二和三兩步,直到集合F中只有一棵二叉樹爲止。

簡易的理解就是,假如我有A,B,C,D,E五個字符,出現的頻率(即權值)分別爲5,4,3,2,1,那麼我們第一步先取兩個最小權值作爲左右子樹構造一個新樹,即取1,2構成新樹,其結點爲1+2=3,如圖:

12

虛線爲新生成的結點,第二步再把新生成的權值爲3的結點放到剩下的集合中,所以集合變成{5,4,3,3},再根據第二步,取最小的兩個權值構成新樹,如圖:

13

再依次建立哈夫曼樹,如下圖:

14

其中各個權值替換對應的字符即爲下圖:

15

按照左子樹0,右子樹1,遍歷整棵樹,就可以將各葉子節點對應的編碼寫出來:

所以各字符對應的編碼爲:A->11, B->10, C->00, D->011, E->010

霍夫曼編碼是一種無前綴編碼。解碼時不會混淆。其主要應用在數據壓縮,加密解密等場合。

C語言代碼實現:

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


#define MAXVALUE  10000  /*定義最大權值*/
#define MAXLEAF     30  /*定義哈夫曼樹葉結點個數*/
#define MAXNODE    MAXLEAF*2 -1
#define MAXBIT      100 /*定義哈夫曼編碼的最大長度*/


//--------------------------------------------------------------------//
typedef struct
{
    int bit[MAXBIT];
    int start;
} HCodeType;

typedef struct
{
    int weight;
    int parent;
    int lchild;
    int rchild;
    int value;
} HNodeType;

//--------------------------------------------------------------------//
//C語言實現哈弗曼樹編碼
typedef struct
{
    int bit[MAXBIT];
    int start;
} HCODETYPE;

typedef struct
{
    int weight;
    int parent;
    int lchild;
    int rchild;
} HNODETYPE;

char *getcode1(char *s1, char *s2, char *s3) /*首先去掉電文中的空格*/
{
    char temp[128] = "", *p, *q;
    p = s1;
    while ((q = strstr(p, s2)) != NULL)
    {
        *q = '\0';
        strcat(temp, p);
        strcat(temp, s3);
        p = q + strlen(s2);
    }
    strcat(temp, p);
    strcpy(s1, temp);
    return s1;
}

/*再去掉重複出現的字符(即壓縮電文),提取哈夫曼樹葉結點*/
char * getcode(char *s1)
{
    char s2[26], s5[26];
    char temp[200] = "", *p, *q, *r, *s3 = "";
    int m, e, n = 0;
    m = strlen(s1);
    while (m > 0)
    {
        p = s1;
        s2[0] = s1[0];
        s2[1] = '\0';
        r = s2;
        e = 0;
        while ((q = strstr(p, r)) != NULL)
        {
            *q = '\0';
            strcat(temp, p);
            strcat(temp, s3);
            p = q + strlen(s2);
            e++;
        }
        m -= e;
        strcat(temp, p);
        strcpy(s1, temp);
        s5[n] = s2[0];
        n++;
        strcpy(temp, "");
    }
    s5[n] = '\0';
    strcpy(s1, s5);
    printf(" 壓縮後的電文(即葉結點): %s\n", s1);
    return s1;
}


HNODETYPE huffmantree(char *s2, char s3[])
{
    HNODETYPE huffnode[MAXNODE];
    HCODETYPE huffcode[MAXLEAF], cd;
    int sum, i, j, n1, n2, x1, x2, p, k, c;
    char s1[26] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m',
        'n','o','p','q','r','s','t','u','v','w','x','y','z' };
    char s5[MAXLEAF];
    int ww[26] = { 0 }, n = 0;
    strcpy(s5, s2);
    sum = strlen(s2);
    for (i = 0; i < 26; i++) /*統計所有字符出現的頻度*/
    {
        for (j = 0; j < sum; j++)
        {
            if (s2[j] == s1[i])
            {
                ww[i]++;
            }
        }
    }
    n = strlen(s3);
    for (i = 0; i < 2 * n - 1; i++)
    {
        huffnode[i].weight = 0;
        huffnode[i].parent = -1;
        huffnode[i].lchild = -1;
        huffnode[i].rchild = -1;
    }
    for (i = 0; i < n; i++) /*分配給各葉結點權值*/
    {
        for (j = 0; j < 26; j++)
        {
            if (s3[i] == s1[j])
            {
                huffnode[i].weight = ww[j];
            }
        }
    }

    for (i = 0; i < n - 1; i++) /*構造哈夫曼樹*/
    {
        n1 = n2 = MAXVALUE;
        x1 = x2 = 0;
        for (j = 0; j < n + i; j++)
        {
            if (huffnode[j].parent == -1 && huffnode[j].weight < n1)
            {
                n2 = n1;
                x2 = x1;
                n1 = huffnode[j].weight;
                x1 = j;
            }
            else if (huffnode[j].parent == -1 && huffnode[j].weight < n2)
            {
                n2 = huffnode[j].weight; x2 = j;
            }
        }
        //
        huffnode[x1].parent = n + i;
        huffnode[x2].parent = n + i;
        huffnode[n + i].weight = huffnode[x1].weight + huffnode[x2].weight;
        huffnode[n + i].lchild = x1;
        huffnode[n + i].rchild = x2;
    }

    for (i = 0; i < n; i++) /*求每個葉結點的哈夫曼編碼*/
    {
        cd.start = n - 1;
        c = i;
        p = huffnode[c].parent;
        while (p != -1)
        {
            if (huffnode[p].lchild == c)
            {
                cd.bit[cd.start] = 0;
            }
            else
            {
                cd.bit[cd.start] = 1;
            }
            cd.start--;
            c = p;
            p = huffnode[c].parent;
        }
        for (j = cd.start + 1; j < n; j++)
        {
            huffcode[i].bit[j] = cd.bit[j];
        }
        huffcode[i].start = cd.start;
    }
    printf(" 各葉結點對應哈夫曼編碼 : ");/*輸出每個葉結點的哈夫曼編碼*/
    for (i = 0; i < n; i++)
    {
        for (j = huffcode[i].start + 1; j < n; j++)
        {
            printf("%d", huffcode[i].bit[j]);
        }
        printf(" ");
    }
    printf("\n 電文的全部哈夫曼編碼 : ");/*輸出電文的全部哈夫曼編碼*/
    for (k = 0; k < sum; k++)
    {
        for (i = 0; i < n; i++)
        {
            if (s2[k] == s3[i])
            {
                for (j = huffcode[i].start + 1; j < n; j++)
                {
                    printf("%d", huffcode[i].bit[j]);
                }
                printf(" ");
            }
        }
    }
    printf("\n");

}

void main1()
{
    char s1[MAXLEAF], s2[MAXLEAF];
    printf("\n 請輸入電文 : ");
    //gets(s1);
    strcpy(s1, "a b c d e");
    strcpy(s2, getcode1(s1, " ", ""));
    huffmantree(s1, getcode(s2));
}
//--------------------------------------------------------------------//

//--------------------------------------------------------------------//
//
void HuffmanTree(HNodeType HuffNode[MAXNODE], int n)
{
    int i, j, m1, m2, x1, x2;
    char nvalue[5] = { 'A','B','C','D','E' };//葉子節點n
    int qvalue[5] = {5, 4, 3, 2, 1};

    //總共樹的節點爲2n-1
    for (i = 0; i < 2 * n - 1; i++)
    {
        HuffNode[i].weight = 0;//權值 
        HuffNode[i].parent = -1;
        HuffNode[i].lchild = -1;
        HuffNode[i].rchild = -1;
        HuffNode[i].value = i; //實際值,可根據情況替換爲字母
    }

    for (i = 0; i < n; i++)
    {
        //printf("Please input weight of leaf node %d: \n", i);
        //scanf("%d", &HuffNode[i].weight);
        HuffNode[i].weight = n - i;
        HuffNode[i].value = nvalue[i];
        printf("the weight of leaf node %d is %d: \n", i, HuffNode[i].weight);
    }

    for (i = 0; i < n - 1; i++)//構造哈弗曼樹
    {
        m1 = m2 = MAXVALUE;
        x1 = x2 = 0;
        
        for (j = 0; j < n + i; j++) //找出最小的節點權重
        {
            if (HuffNode[j].weight < m1 && HuffNode[j].parent == -1)
            {
                m2 = m1;
                x2 = x1;
                m1 = HuffNode[j].weight;
                x1 = j;
            }
            else if (HuffNode[j].weight < m2 && HuffNode[j].parent == -1)
            {
                m2 = HuffNode[j].weight;
                x2 = j;
            }
        }

        HuffNode[x1].parent = n + i;
        HuffNode[x2].parent = n + i;
        HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight;
        HuffNode[n + i].lchild = x1;
        HuffNode[n + i].rchild = x2;

        printf("x1.weight and x2.weight in round %d: %d, %d\n", i + 1, HuffNode[x1].weight, HuffNode[x2].weight);
        //printf("\n");
    }
}

//解碼 
void decodeing(char string[], HNodeType Buf[], int Num)
{
    int i, tmp = 0, code[1024];
    int m = 2 * Num - 1;
    char *nump;
    char num[1024];

    for (i = 0; i < strlen(string); i++)
    {
        if (string[i] == '0')
        {
            num[i] = 0;
        }
        else
        {
            num[i] = 1;
        }
    }

    i = 0;
    nump = &num[0];

    while (nump < (&num[strlen(string)]))
    {
        //從根節點遍歷哈弗曼樹
        tmp = m - 1;
        while ((Buf[tmp].lchild != -1) && (Buf[tmp].rchild != -1))
        {
            if (*nump == 0)
            {
                tmp = Buf[tmp].lchild;
            }
            else
            {
                tmp = Buf[tmp].rchild;
            }
            nump++;
        }
        printf("%d(%c)", Buf[tmp].value, Buf[tmp].value);
    }
}


int main2(void)
{
    HNodeType HuffNode[MAXNODE];
    HCodeType HuffCode[MAXLEAF], cd;

    int i, j, c, p, n;
    char pp[100];    

    //printf("Please input n:\n");
    //scanf("%d", &n);
    n = 5;
    printf("the node num of huffman tree is %d\n", n);

    //哈弗曼樹
    HuffmanTree(HuffNode, n);

    //哈夫曼編碼
    for (i = 0; i < n; i++)
    {
        cd.start = n - 1;
        c = i;
        p = HuffNode[c].parent;

        while (p != -1)
        {
            if (HuffNode[p].lchild == c) {
                cd.bit[cd.start] = 0;
            } else {
                cd.bit[cd.start] = 1;
            }
            //
            cd.start--;
            c = p;
            p = HuffNode[c].parent;
        }

        for (j = cd.start + 1; j < n; j++)//
        {
            HuffCode[i].bit[j] = cd.bit[j];
        }

        HuffCode[i].start = cd.start;
    }

    for (i = 0; i < n; i++)//
    {
        printf("%d 's Huffman code is: ", i);
        for (j = HuffCode[i].start + 1; j < n; j++)
        {
            printf("%d", HuffCode[i].bit[j]);
        }
        printf(" start:%d", HuffCode[i].start);
        printf("\n");
    }

    printf("\n");
    printf("Decoding? Please Enter code:\n");
    scanf("%s", &pp);

    decodeing(pp, HuffNode, n);

    getchar();

    return 0;
}

運行結果:

 

 

發佈了59 篇原創文章 · 獲贊 15 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章