哈弗曼树与哈弗曼编码简介

树和哈夫曼编码。哈夫曼编码是哈夫曼树的一个应用。哈夫曼编码应用广泛,如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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章