欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 19:05:30
...

树和哈夫曼编码。哈夫曼编码是哈夫曼树的一个应用。哈夫曼编码应用广泛,如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,如图:

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

虚线为新生成的结点,第二步再把新生成的权值为3的结点放到剩下的集合中,所以集合变成{5,4,3,3},再根据第二步,取最小的两个权值构成新树,如图:

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

再依次建立哈夫曼树,如下图:

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

其中各个权值替换对应的字符即为下图:

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

按照左子树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;
}

运行结果:

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