哈夫曼树

Posted xiaoshuita

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哈夫曼树相关的知识,希望对你有一定的参考价值。

这里讲的哈夫曼树有创建哈夫曼树,输出哈夫曼树,递归进行哈夫曼树编码,哈夫曼解码这些功能。

1.创建哈夫曼树:(函数参数为整型数组)

(1)引入哈夫曼树指针数组并申请空间,为每棵哈夫曼树复制,将其左右节点赋值为NULL。

(2)将(n-1)棵哈夫曼树合并:a.引入两个整形变量始终代表最小和次小的下标

                                                    b.比较权值,使两个下标成为最小的两个权值的下标

                                                    c.合并一次,并将最小下标的哈夫曼树赋值为新的哈夫曼树,次小下表的哈夫曼树赋值为空。

2.输出哈夫曼树:根节点不为空,输出权重,若左右子树不为空,依次输出“(”, 左子树,“,”,右子树,“)”

3.递归进行哈夫曼编码:由于哈夫曼树的特点是所有的原始树都为叶子节点,故可以用这方法来进行编码。

4.哈夫曼解码:while(已解码长度<字符串长度){找到某一叶子节点并打印}

 

以下是完整代码:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef int ELEMTYPE;

// 哈夫曼树结点结构体

typedef struct HuffmanTree

{

    ELEMTYPE weight;

    ELEMTYPE id;        // id用来主要用以区分权值相同的结点,这里代表了下标

    struct HuffmanTree* lchild;

    struct HuffmanTree* rchild;

}HuffmanNode;

 

// 构建哈夫曼树

HuffmanNode* createHuffmanTree(int* a, int n)

{

    int i, j;

    HuffmanNode **temp, *hufmTree;

    temp = (HuffmanNode **)malloc(n*sizeof(HuffmanNode));

    for (i=0; i<n; ++i)     // 将数组a中的权值赋给结点中的weight

    {

        temp[i] = (HuffmanNode*)malloc(sizeof(HuffmanNode));

        temp[i]->weight = a[i];

        temp[i]->id = i;

        temp[i]->lchild = temp[i]->rchild = NULL;

    }

 

    for (i=0; i<n-1; ++i)       // 构建哈夫曼树需要n-1合并

    {

        int small1=-1, small2;      // small1small2分别作为最小和次小权值的下标

        for (j=0; j<n; ++j)         // 先将最小的两个下标赋给small1small2(注意:对应权值未必最小)

        {

            if (temp[j] != NULL && small1==-1) 

            {

                small1 = j;

                continue;

            } else if(temp[j] != NULL)

            {

                small2 = j;

                break;

            }

        }

 

        for (j=small2; j<n; ++j)    // 比较权值,挪动small1small2使之分别成为最小和次小权值的下标

        {

            if (temp[j] != NULL)   

            {

                if (temp[j]->weight < temp[small1]->weight)

                {

                    small2 = small1;

                    small1 = j;

                } else if (temp[j]->weight < temp[small2]->weight)

                {

                    small2 = j;

                }

            }

        }

        hufmTree = (HuffmanNode*)malloc(sizeof(HuffmanNode));

        hufmTree->weight = temp[small1]->weight + temp[small2]->weight;

        hufmTree->lchild = temp[small1];

        hufmTree->rchild = temp[small2];

 

        temp[small1] = hufmTree;

        temp[small2] = NULL;

    }

    free(temp);

    return hufmTree;

}

 

// 以广义表的形式打印哈夫曼树

void PrintHuffmanTree(HuffmanNode* hufmTree)

{

    if (hufmTree)

    {

        printf("%d", hufmTree->weight);

        if (hufmTree->lchild != NULL || hufmTree->rchild != NULL)

        {

            printf("(");

            PrintHuffmanTree(hufmTree->lchild);

            printf(",");

            PrintHuffmanTree(hufmTree->rchild);

            printf(")");

        }

    }

}

 

// 递归进行哈夫曼编码

void HuffmanCode(HuffmanNode* hufmTree, int depth)      // depth是哈夫曼树的深度

{

    static int code[10];

    if (hufmTree)

    {

        if (hufmTree->lchild==NULL && hufmTree->rchild==NULL)

        {

            printf("id%d权值为%d的叶子结点的哈夫曼编码为 ", hufmTree->id, hufmTree->weight);

            int i;

            for (i=0; i<depth; ++i)

            {

                printf("%d", code[i]);

            }

            printf(" ");

        } else

        {

            code[depth] = 0;

            HuffmanCode(hufmTree->lchild, depth+1);

            code[depth] = 1;

            HuffmanCode(hufmTree->rchild, depth+1);

        }

    }

}

 

// 哈夫曼解码

void HuffmanDecode(char ch[], HuffmanNode* hufmTree, char string[])     // ch是要解码的01串,string是结点对应的字符

{

    int i;

    int num[100];

    HuffmanNode* tempTree = NULL;

    for (i=0; i<strlen(ch); ++i)

    {

        if (ch[i] == ‘0‘)

            num[i] = 0;

        else

            num[i] = 1;

    }

    if(hufmTree)

    {

        i = 0;      // 计数已解码01串的长度

        while(i<strlen(ch))

        {

            tempTree = hufmTree;

            while(tempTree->lchild!=NULL && tempTree->rchild!=NULL)

            {

                if (num[i] == 0)

                {

                    tempTree = tempTree->lchild;

                } else

                {

                    tempTree = tempTree->rchild;

                }

                ++i;

            }

            printf("%c", string[tempTree->id]);     // 输出解码后对应结点的字符

        }

    }

}

 

int main()

{

    int i, n;

    printf("请输入叶子结点的个数: ");

    while(1)

    {

        scanf("%d", &n);

        if (n>1)

            break;

        else

            printf("输入错误,请重新输入n值!");

    }

 

    int* arr;

    arr=(int*)malloc(n*sizeof(ELEMTYPE));

    printf("请输入%d个叶子结点的权值: ", n);

    for (i=0; i<n; ++i)

    {

        scanf("%d", &arr[i]);

    }

 

    char ch[100], string[100];

    printf("请连续输入这%d个叶子结点各自所代表的字符: ", n);

    fflush(stdin);      // 强行清除缓存中的数据,也就是上面输入权值结束时的回车符

    gets(string);

 

    HuffmanNode* hufmTree = NULL;

    hufmTree = createHuffmanTree(arr, n);

 

    printf("此哈夫曼树的广义表形式为: ");

    PrintHuffmanTree(hufmTree);

    printf(" 各叶子结点的哈夫曼编码为: ");

    HuffmanCode(hufmTree, 0);

 

    printf("要解码吗?请输入编码: ");

    gets(ch);

    printf("解码结果为: ");

    HuffmanDecode(ch, hufmTree, string);

    printf(" ");

 

    free(arr);

    free(hufmTree);

 

    return 0;

}

 

以上是关于哈夫曼树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构—— 树:哈夫曼树和哈夫曼编码

哈夫曼树

哈夫曼树中的“权值”是指啥?

哈夫曼树+哈夫曼编码

数据结构中哈夫曼树的应用(C语言)

如何构造哈夫曼树