栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

6.2哈夫曼树编码与解码

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

6.2哈夫曼树编码与解码

哈夫曼树相关的几个名词

路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径。图 1 中,从根结点到结点 a 之间的通路就是一条路径。

路径长度:在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1 。图 1 中从根结点到结点 c 的路径长度为 3。

结点的权:给每一个结点赋予一个新的数值,被称为这个结点的权。例如,图 1 中结点 a 的权为 7,结点 b 的权为 5。

结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积。例如,图 1 中结点 b 的带权路径长度为 2 * 5 = 10 。

树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL” 。例如图 1 中所示的这颗树的带权路径长度为:

WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3

构建哈夫曼树的过程

对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:

  1. 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
  2. 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
  3. 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。

 

 

 图 2 中,(A)给定了四个结点a,b,c,d,权值分别为7,5,2,4;第一步如(B)所示,找出现有权值中最小的两个,2 和 4 ,相应的结点 c 和 d 构建一个新的二叉树,树根的权值为 2 + 4 = 6,同时将原有权值中的 2 和 4 删掉,将新的权值 6 加入;进入(C),重复之前的步骤。直到(D)中,所有的结点构建成了一个全新的二叉树,这就是哈夫曼树。

全部代码:

#include  
#include 
#include 

#define MAXSIZE 1024  // 读入文件的上限 
#define OK 1
#define ERROR 0
typedef int Status;


typedef struct wordcnt
{
    char ch;
    int cnt = 0;
} Count;


typedef struct NumCount
{
    Count count[MAXSIZE];
    int length = 0;
} NumCount;


typedef struct HTree
{
    char data;
    int weight;
    int parent, leftChild, rightChild;
} HTNode, * HuffmanTree;


typedef struct HCode
{
    char data;
    char* str;
} *HuffmanCode, HCode;


Status ReadData(char* source)
{
    char ch;
    int i = 0;
    FILE* pFile;
    pFile = fopen("myfile.txt", "r");
    printf("正在读取文件n");
    ch = fgetc(pFile);
    if (ch == EOF)
    {
        printf("文件为空n");
        fclose(pFile);
        return ERROR;
    }
    fclose(pFile);
    pFile = fopen("myfile.txt", "r");
    printf("文件内容是:");
    while ((ch = fgetc(pFile)) != EOF)
    {
        source[i++] = ch;
    }
    source[i] = '';
    printf("%sn", source);
    fclose(pFile);
    return OK;
}


Status WordCount(char* data, NumCount* tempCnt)
{
    int flag;   //判断是否已经记录
    int len = strlen(data);
    for (int i = 0; i < len; ++i)
    {
        flag = 0;
        for (int j = 0; j < tempCnt->length; ++j)
        {
            if (tempCnt->count[j].ch == data[i]) //若已有记录,直接++
            {
                ++tempCnt->count[j].cnt;
                flag = 1;
                break;
            }
        }
        if (!flag)   //若未记录,则新增
        {
            tempCnt->count[tempCnt->length].ch = data[i];
            ++tempCnt->count[tempCnt->length].cnt;
            ++tempCnt->length;
        }
    }
    return OK;
}


Status Show(NumCount* tempCnt)
{
    printf("长度为%dn", tempCnt->length);
    for (int i = 0; i < tempCnt->length; ++i)
    {
        printf("字符%c出现%d次n", tempCnt->count[i].ch, tempCnt->count[i].cnt);
    }
    printf("n");
    return OK;
}


Status select(HuffmanTree HT, int top, int* s1, int* s2)
{
    int min = INT_MAX;
    for (int i = 1; i <= top; ++i)   //选择没有双亲的结点中,权重最小的结点
    {
        if (HT[i].weight < min && HT[i].parent == 0)
        {
            min = HT[i].weight;
            *s1 = i;
        }
    }
    min = INT_MAX;
    for (int i = 0; i <= top; ++i)  //选择没有双亲的结点中,权重次小的结点
    {
        if (HT[i].weight < min && i != *s1 && HT[i].parent == 0)
        {
            min = HT[i].weight;
            *s2 = i;
        }
    }
    return OK;
}


Status CreateHuffmanTree(HuffmanTree& HT, int length, NumCount cntarray)
{
    if (length <= 1)
        return ERROR;
    int s1, s2;
    int m = length * 2 - 1;   //没有度为1的结点,则总结点数是2 * 叶子结点数 - 1个
    HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));
    for (int i = 1; i <= m; ++i)    //初始化
    {
        HT[i].parent = 0;
        HT[i].leftChild = 0;
        HT[i].rightChild = 0;
    }

    for (int i = 1; i <= length; ++i)
    {
        HT[i].data = cntarray.count[i - 1].ch;
        HT[i].weight = cntarray.count[i - 1].cnt;
    }
    for (int i = length + 1; i <= m; ++i)
    {
        select(HT, i - 1, &s1, &s2);    //从前面的范围里选中权重最小的两个结点
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].leftChild = s1;
        HT[i].rightChild = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;   //得到一个新结点
    }
    return OK;
}


Status CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int length)
{
    HC = (HuffmanCode)malloc(sizeof(HCode) * (length + 1));
    char* cd = (char*)malloc(sizeof(char) * length);    //存储代码的临时空间
    cd[length - 1] = '';    //方便之后调用strcpy函数
    int c, f, start;
    for (int i = 1; i <= length; ++i)
    {
        start = length - 1;   //start表示编码在临时空间内的起始下标,由于是从叶子节点回溯,所以是从最后开始 
        c = i;
        f = HT[c].parent;
        while (f != 0)
        {
            --start;    //由于是回溯,所以从临时空间的最后往回计
            if (HT[f].leftChild == c)
                cd[start] = '0';
            else
                cd[start] = '1';
            c = f;
            f = HT[c].parent;
        }
        HC[i].str = (char*)malloc(sizeof(char) * (length - start)); // 最后,实际使用的编码空间大小是length-start
        HC[i].data = HT[i].data;
        strcpy(HC[i].str, &cd[start]);  // 从实际起始地址开始,拷贝到编码结构中 
    }
    free(cd);
}


Status Encode(char* data, HuffmanCode HC, int length)
{
    FILE* pFile;
    pFile = fopen("mycode.txt", "w");
    for (int i = 0; i < strlen(data); ++i)   //依次读入数据,查找对应的编码,写入编码文件
    {
        for (int j = 1; j <= length; ++j)
        {
            if (data[i] == HC[j].data)
            {
                fputs(HC[j].str, pFile);
            }
        }
    }
    fclose(pFile);
    printf("编码文件已写入n");
    return OK;
}


Status Decode(HuffmanTree HT, int length)
{
    char* codetxt = (char*)malloc(sizeof(char) * (MAXSIZE * length));
    FILE* pFile;
    pFile = fopen("mycode.txt", "r");
    fgets(codetxt, MAXSIZE * length, pFile);
    fclose(pFile);

    FILE* outfile;
    outfile = fopen("mytxt.txt", "w");
    int root = 2 * length - 1;    //从根结点开始遍历
    for (int i = 0; i < strlen(codetxt); ++i)
    {
        if (codetxt[i] == '0')
            root = HT[root].leftChild;  //为0表示向左遍历
        else if (codetxt[i] == '1')
            root = HT[root].rightChild; //为1表示向右遍历
        if (HT[root].leftChild == 0 && HT[root].rightChild == 0)  //如果已经是叶子结点,输出到文件中,然后重新返回到根结点
        {
            putc(HT[root].data, outfile);
            root = 2 * length - 1;
        }
    }
    fclose(outfile);
    printf("输出文件已写入n");
    return OK;
}


void test()
{
    char data[MAXSIZE];
    NumCount Cntarray;
    ReadData(data); //读入数据
    WordCount(data, &Cntarray); //统计次数
  //Show(&Cntarray);    //查看每个单词出现的次数
    HuffmanTree tree;
    CreateHuffmanTree(tree, Cntarray.length, Cntarray);   //建树
    HuffmanCode code;
    CreateHuffmanCode(tree, code, Cntarray.length); //创建编码
    Encode(data, code, Cntarray.length);    //生成编码文件
    Decode(tree, Cntarray.length);  //解码
    printf("请查看生成的TXT文件以检查结果n");
    return;
}

int main()
{
    test();
    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934679.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号