哈弗曼树 C语言

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#define N 27 //带权值的叶子节点个数或者是需要编码的字符数

#define M 2*N-1//n个叶子节点构造的哈弗曼树有2n-1个结点(因为该树只有叶子和度为2的结点)

#define MAX 10000

typedef struct

{

    unsigned int weight; //权值只能是正数

    unsigned int parent, lchild, rchild;

}HTNode, *HuffmanTree;

typedef char **HuffmanCode;//存储每个字符的哈夫曼编码表 是一个字符指针数组 每个数组元素是指向字符指针的指针

void Select(HuffmanTree HT, int k, int &s1, int &s2);

void PrintTree(HuffmanTree HT, char ch[]);

void HuffmanCoding(HuffmanTree &HT,  int *w, int n);

void PrintCode(HuffmanCode HC, char ch[]);

void CreatTree(HuffmanTree &HT, int *w, int n);

void getCode1(HuffmanTree HT, HuffmanCode &HC);

void decodeHuffmanTree(HuffmanTree HT, HuffmanCode, char *ch, int len);

void getCode(HuffmanCode &code, char *ch, char *words, HuffmanCode &HC);

int main ()

{

    HuffmanTree HT;char *codes;

    int m=0;

    HuffmanCode HC, HC1;//HC有n个元素 每个元素是一个指向字符串的指针 即每个元素是一个char *的变量

    char ch[N+1]={‘0′,’ ‘, ‘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’};//每个元素与下面的权值相对应 第一个元素不使用留空

    int w[N+1]={ 0,   186,  64,  13,  22,  32,103, 21, 15, 47, 57, 1,  5,  32, 20, 57, 63, 15, 1, 48, 51,80,23,8,18,1,16};

    char *word=”0this program is my favorite”;

    HuffmanCode code;

    HuffmanCoding(HT, w, N);//建立哈弗曼树并且求出哈弗曼编码

    getCode1(HT,HC);

    PrintTree(HT,ch);

    PrintCode(HC, ch);

    getCode(code, ch, word, HC);//子串的编码 原串 子串 原串的哈弗曼编码

    PrintCode(code, word);

    decodeHuffmanTree(HT, code, ch, strlen(word));

}

void HuffmanCoding(HuffmanTree &HT,  int *w, int n)

{//首先构造哈弗曼树

    int i, s1, s2;

    if(n<=1)

        return;

    HT=(HuffmanTree)malloc((M+1)*sizeof(HTNode));//0号单元不使用 而树中有n个结点所以要+1

    for(i=1; i<=N; i++)

    {

        HT[i]={w[i], 0, 0, 0};//HT的前n个分量存储叶子节点  均带有权值

    }

    for(;i<=M; ++i)  //HT后m-n个分量存储中间结点 最后一个分量是整棵树的根节点

    {

        HT[i]={0, 0, 0, 0};

    }

    for(i=N+1; i<=M; i++)//开始构建哈弗曼树 即创建HT的后m-n个节点的过程,直到创建出根节点 使用哈夫曼算法

    {//i的初始值为N+1  树的前N个位置均有数了

        Select(HT, i-1, s1, s2);//在HT[1……i-1]中选择parent为0且权值最小的两个结点, 其序号分别为s1, s2

                                //若parent不为0 则说明该结点已经参与了构造 因此不能再次考虑 相当于将该节点从树中排除

                                //而i不断增加 意味着将新增的 中间节点 也考虑进去了

        HT[s1].parent=i;    HT[s2].parent=i;//将两节点标记为已访问 并且将他们的parent的位置确定下来

                                            //两个最小的结点的祖先是同一个

        HT[i].lchild=s1;    HT[i].rchild=s2;//将两个最小值赋给HT的第N+1个结点(两结点的共同祖先的左右孩子确定了)

                                            //在当前数组的末尾新增了中间节点

                                            //循环遍历之后lchild和rchild中存储的是在HT数组中的下标的值

        HT[i].weight=HT[s1].weight+HT[s2].weight; //为中间节点的权值赋值便于下一次参与权值的比较

    }

}

void getCode1(HuffmanTree HT, HuffmanCode &HC)//从叶子到根求编码

{

    char *cd;

    int start, c, f, i;

    HC=(HuffmanCode)malloc((N+1)*sizeof(char *));

    cd=(char *)malloc(N*sizeof(char));

    cd[N-1]=’\0′;//标记了tmp中最后一个元素

    for(i=1; i<=N; i++)//从第一个元素开始对全部N

    {

        start=N-1;//将start的位置设置为原有所有叶子节点(未包括中间节点)最后一个元素的下标

                    //每次循环进行对cd而言都是在叶子开始  因为从叶子开始编码但是从根开始显示

        for(c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent)//f!=0决定了进行操作的必须是已经筛选出来的元素

            {                    //该循环从下向上进行 c和f逐个向上移动 即实现了从叶子到根的编码

                                                    //当进行到f=0的时候即为根节点 之前0号单元未被占用 因此可以判断走到了根

                if(HT[f].lchild==c)             //此时是对HT的左右孩子进行判断

                                            //左孩子是0

                cd[–start]=’0′;

            else// 即为HT的右孩子是c  此时cd的值是1

                cd[–start]=’1′;

            }

        HC[i]=(char*)malloc((N-start)*sizeof(char));//因为HC[i]是HC的一个元素 而HC是二维指针数组

                                                    //其中的每个元素都是一个一维指针数组  因此可以每次单独分配空间

                                                    //空间的大小就是由每次的start决定的

                                                    // 因为start从N-1开始所以数组的大小为N-start

        strcpy(HC[i], &cd[start]);   //将cd中的元素全部拷贝到当前大小的HC[i]中

    }

    free(cd);//结束之后 HC中包含多个数组元素每个数组元素中有对应位置原数据的编码

}

void getCode(HuffmanCode &code, char *ch, char *words, HuffmanCode &HC)//子串的编码 原串 子串 原串的哈弗曼编码

{

    int i, j, k=0, now=0, m, number=0;

    char *c;

    code=(HuffmanCode)malloc((strlen(words)+1)*sizeof(char *));

    for(i=1; i<=strlen(words); i++)

    {

        for(j=1; j<=N; j++)

        {

            if(words[i]==ch[j])

            {

                code[i]=(char*)malloc((strlen(HC[j]))*sizeof(char));

                strcpy(code[i], HC[j]);//printf(“<%s>”, code[i]);

            }

        }

    }

}

void decodeHuffmanTree(HuffmanTree HT,HuffmanCode code, char *word, int len)

{

    int p=M;

    int i=0, j=1;

    while(j<=N)

    {

        while(i<strlen(code[j]))

        {

            if(code[j][i]==’0′)

                    p=HT[p].lchild;

            if(code[j][i]==’1′)

                  p=HT[p].rchild;

            if(HT[p].lchild==0 && HT[p].rchild==0)

                {

                   printf(“%c”,word[p]);

                     p=M;

                }

            i++;

        }

         j++;

         i=0;

    }

}

void Select(HuffmanTree HT, int k, int &s1, int &s2)

{//在HT[1……i-1]中选择parent为0且权值最小的两个结点, 其序号分别为s1, s2

    //若parent不为0 则说明该结点已经参与了构造 因此不能再次考虑 相当于将该节点从树中排除

    //假设s1对应的权值总是<=s2的权值

    unsigned int tmp=MAX, tmpi=0;

    int i;

    for(i=1; i<=k; i++)

    {

        if(!HT[i].parent)//parent必须是0 表示没有访问过

        {

            if(tmp>HT[i].weight)

                {

                    tmp=HT[i].weight;

                    tmpi=i;

                }

        }

    }//循环结束后tmp是最小的weight权值

    s1=tmpi;//此时s1时拥有最小权值的节点的下标

    tmp=MAX;//为tmp设定一个最大值 避免在与树中的结点进行比较的时候 初始值小于树中的结点而导致比较失败

    tmpi=0;//将所求结点的下标初始化

    for(i=1; i<=k; i++)

    {

        if((!HT[i].parent)&&i!=s1)//parent不为零 保证所找结点未曾使用过

        {

            if(tmp>HT[i].weight)

            {

                tmp=HT[i].weight;

                tmpi=i;

            }

        }

    }

    s2=tmpi;//s2中存储了第二个所求结点的下标

}

void PrintTree(HuffmanTree HT, char ch[])

{

    printf(“\ndata, weight, parent, lchild, rchild\n”);

    for(int i=1; i<=M; i++)

    {

        if(i>N)

        {

            printf(” -, %5d, %5d, %5d, %5d\n”, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);

        }

        else

            printf(” %c, %5d, %5d, %5d, %5d\n”, ch[i], HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);

    }

    printf(“\n”);

}

void PrintCode(HuffmanCode HC, char ch[])

{

    printf(“\n”);

    for(int i=1; i<=N; i++)

        printf(“%c:%s\n”, ch[i], HC[i]);

    printf(“\n”);

}

    原文作者:哈夫曼树
    原文地址: https://blog.csdn.net/sinat_40432951/article/details/78408924
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞