【数据结构--哈夫曼编码(C语言版)】

这篇具有很好参考价值的文章主要介绍了【数据结构--哈夫曼编码(C语言版)】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

哈夫曼树及其应用

哈夫曼树

介绍哈夫曼树前先介绍下面几个名词:

1. 结点的路径长度l

从根结点到该结点的路径上分支的数目,如下图结点al = 3

2. 树的路径长度

树中所有叶子结点的路径长度之和,如下图该树的路径长度为2 + 3 + 3 + 2 + 2

3. 结点的权w

给每一个结点赋予一个新的数值,称为这个结点的权。

4. 结点的带权路径长度l * w

从根结点到该结点之间的路径长度与该结点的权的乘积,下图结点a的带权路径长度为l * w = 3

5. 树的带权路径长度 WPL = ∑li * wi

树中所有叶子结点的带权路径长度之和。

【数据结构--哈夫曼编码(C语言版)】
带权路径长度WPL最小的二叉树称为哈夫曼树(又称为最优二叉树)。

哈夫曼树的特点

  • 权值小的结点离根远,权值大的结点离根近。
  • 结点的度:没有度为1的结点。

哈夫曼树的构造

思路:

1. 对于给定的有各自权值的n个结点,从n个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新的二叉树的根结点的权值为左右孩子权值的和

2. 在原有的n个权值中删除那两个最小的权值,同时将新的权值加入到n-2个权值的行列中,以此类推

3. 重复1和2,直到所有的结点构建成了一棵二叉树为止,该树即为哈夫曼树。

按权构造哈夫曼树的过程如下图

【数据结构--哈夫曼编码(C语言版)】

哈夫曼编码

一般地,设需要编码的字符集为{d1, d2, …… ,dn},各个字符在电文中出现的次数或频率集合为{w1, w2, …… , wn},以d1,d2,…… ,dn作为叶子结点,以w1,w2,…… ,wn作为相应叶子结点的权值来构造一棵哈夫曼树。规定哈夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,这就是哈夫曼编码。

例如有一段文字内容"WINNIE WILL WIN"其中W的权值为3I的权值为4N的权值为3E的权值为1L的权值为2 ,则其按哈夫曼树规划如下。

哈夫曼编码
W I N E L
3 4 3 1 2
00 11 10 010 011
  • 构造以W(3)I(4)N(3)E(1)L(2)为叶子结点的哈夫曼树
  • 将该二叉树所有左分枝标记0,所有右分枝标记1
  • 根结点到叶子结点所经过的二进制序列为该叶子结点字符的编码

【数据结构--哈夫曼编码(C语言版)】

哈夫曼树结构

/* 哈夫曼树结构 */
typedef struct HTree
{
    char data;
    int weight;
    int parent,leftChild,rightChild;
} HTNode, *HuffmanTree;

编码结构

/* 编码结构 */
typedef struct HCode
{
    char data;
    char* str;
} *HuffmanCode, HCode;

统计字符和对应的次数

/* 统计字符和对应的次数 */
typedef struct wordcnt
{
    char ch;
    int cnt = 0;
} Count;

/* 统计次数的外部封装 */
typedef struct NumCount
{
    Count count[MAXSIZE];
    int length = 0;
} NumCount;

读入文件

/* 读入文件 */
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] = '\0';
    printf("%s\n",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("长度为%d\n",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] = '\0';    //方便之后调用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);
}

将读入的文件编码写入txt文件

/* 将读入的文件编码,写到txt文件  */
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;
}

完整代码

#include <stdio.h> 
#include <string.h>
#include <stdlib.h>

#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] = '\0';
    printf("%s\n",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("长度为%d\n",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] = '\0';    //方便之后调用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);
}

/* 将读入的文件编码,写到txt文件  */
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;
}

测试结果

正在读取文件
文件内容是:In love folly is always sweet

I love three things in this world. Sun, moon and you. Sun for morning, moon for night , and you forever .

"In fact, I am very satisfied at least know your name heard your voice to see your eyes."

The furthest distance in the world. Is not being apart while being in love. But when plainly cannot resist the yearning. Yet pretending you have never been in my heart.

编码文件已写入
输出文件已写入
请查看生成的TXT文件以检查结果
myfile.txt

【数据结构--哈夫曼编码(C语言版)】

mycode.txt(内容过长未截完)

【数据结构--哈夫曼编码(C语言版)】

mytxt.txt

【数据结构--哈夫曼编码(C语言版)】文章来源地址https://www.toymoban.com/news/detail-440132.html

到了这里,关于【数据结构--哈夫曼编码(C语言版)】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构之哈夫曼树与哈夫曼编码

    编码是信息处理的基础(重新表示信息)。 普通的编码是等长编码,例如7位的ASCIL编码,对出现频率不同的字符都使用相同的编码长度。 但其在传输和存储等情况下编码效率不高 。 可使用不等长编码,来压缩编码:高频字符编码长度更短,低频字符编码长度更长。   [例

    2023年04月15日
    浏览(64)
  • 数据结构课程实验五:哈夫曼树与哈夫曼编码

    实验日期:2022-12-20   目录 一、实验目的 1、掌握哈夫曼树的建立 2、掌握哈夫曼编码方式 二、实验内容

    2024年02月05日
    浏览(50)
  • 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树(Huffman Tree)以及哈夫曼编码的构造原理、方法,并用代码实现。 路径 :从树中一个结点到另一个结点之间的 分支 构成这两个结点间的路径。 结点的路径长度 :两结点间路径上的 分支数 。 树的路径长度: 从树根到每一个结点的路径长度之和。记作: TL  权

    2024年02月06日
    浏览(53)
  • 【数据结构】实验十:哈夫曼编码

    实验十 哈夫曼编码 一、实验目的与要求 1 )掌握树、森林与二叉树的转换; 2 )掌握哈夫曼树和哈夫曼编码算法的实现; 二、 实验内容 1.  请编程实现如图所示的树转化为二叉树。 2.  编程实现一个哈夫曼编码系统,系统功能包括: (1)  字符信息统计:读取待编码的源文

    2024年02月15日
    浏览(48)
  • 【数据结构与算法】哈夫曼编码(最优二叉树)实现

    哈夫曼编码 等长编码:占的位置一样 变长编码(不等长编码):经常使用的编码比较短,不常用的比较短 最优:总长度最短 最优的要求:占用空间尽可能短,不占用多余空间,且不能有二义性 这里给出哈夫曼二叉树的实现: HuffmanTree.h: 测试数据(主函数): 运行结果截图

    2024年02月16日
    浏览(47)
  • 【数据结构与算法】哈夫曼编码(最优二叉树实现

    哈夫曼编码 等长编码:占的位置一样 变长编码(不等长编码):经常使用的编码比较短,不常用的比较短 最优:总长度最短 最优的要求:占用空间尽可能短,不占用多余空间,且不能有二义性 这里给出哈夫曼二叉树的实现: HuffmanTree.h: 测试数据(主函数): 运行结果截图

    2024年02月16日
    浏览(45)
  • 数据结构 实验17:Huffman树和Huffman编码——学习理解哈夫曼树

    目录 前言 实验要求 算法描述 个人想法 代码实现和思路、知识点讲解 知识点讲解 文件传输 Huffman树的存储 Huffman的构造  Huffman编码 编码和译码 代码实现 文件写入和输出 Huffman树初始化 构造Huffman树 求带权路径长度 Huffman编码 Huffman译码 结束 代码测试 测试结果 利用Huffman编

    2024年02月03日
    浏览(61)
  • 数据结构与算法(C语言版)---哈夫曼编译码器

    1.1、问题阐述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道) ,每端都需要一个完整的编/译

    2024年02月03日
    浏览(89)
  • 数据结构【哈夫曼树】

    最优二叉树也称哈夫曼 (Huffman) 树 ,是指对于一组带有确定权值的叶子结点,构造的具有最小带权路径长度的二叉树。权值是指一个与特定结点相关的数值。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。 涉及到的几个概念: 路径: 从树中一个结点到另一个结

    2024年02月14日
    浏览(41)
  • 数据结构-哈夫曼树

    介绍 哈夫曼树,指 带权路径长度最短的二叉树 ,通常用于数据压缩中 什么是带权路径长度? 假设有一个结点,我们为它赋值,这个值我们称为权值,那么从根结点到它所在位置,所经历的路径,与这个权值的积,就是它的带权路径长度。 比如有这样一棵树,D权值为2  从

    2024年02月20日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包