文章目录

  • 哈夫曼树及其应用
    • 哈夫曼树
    • 哈夫曼树的特点
    • 哈夫曼树的构造
    • 哈夫曼编码

哈夫曼树及其应用

哈夫曼树

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

1. 结点的路径长度l

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

2. 树的路径长度

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

3. 结点的权w

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

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

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

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

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


带权路径长度WPL最小的二叉树称为哈夫曼树(又称为最优二叉树)。

哈夫曼树的特点

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

哈夫曼树的构造

思路:

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

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

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

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

哈夫曼编码

一般地,设需要编码的字符集为{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
  • 根结点到叶子结点所经过的二进制序列为该叶子结点字符的编码

哈夫曼树结构

/* 哈夫曼树结构 */
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';elsecd[start] = '1';c = f;f = HT[c].parent;}HC[i].str = (char*)malloc(sizeof(char) * (length - start)); // 最后,实际使用的编码空间大小是length-startHC[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';elsecd[start] = '1';c = f;f = HT[c].parent;}HC[i].str = (char*)malloc(sizeof(char) * (length - start)); // 最后,实际使用的编码空间大小是length-startHC[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 sweetI 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

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

mytxt.txt

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

  1. 信息论 输入概率的哈夫曼编码 C语言

    信息论 哈夫曼编码 C语言 哈夫曼编码是一种效率比较高的变长无失真信源编码方法.哈夫曼编码的编码方法,步骤如下: 将信源符号按概率从大到小的顺序排列,为方便起见,令p(a1)>=p(a2)> ...

  2. 哈夫曼编码译码 C语言,【求助】严蔚敏版数据结构 哈夫曼编码译码

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #include typedef char* HuffmanCode;/*动态分配数组,存储哈夫曼编码*/ typed ...

  3. 霍夫曼编码PHP,数据结构:哈夫曼编码(php版)

    概述下: 哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩. 在计算机信息处理中,"哈夫曼编码"是一种一致性编码法(又称"熵编码法"),用于 ...

  4. 数据结构哈夫曼编码解码课程设计

    数据结构课程设计 源代码在另一篇博客可以找到:https://blog.csdn.net/qq_40513633/article/details/85055411?ops_request_misc=% ...

  5. 哈夫曼编码c语言论文,哈夫曼编码的实现及应用论文.doc

    哈夫曼编码的实现及应用论文 毕 业 设 计(论文) 题目 哈夫曼编码的实现 及应用 二级学院 数学与统计学院 专 业 信息与计算科学 班 级 学生姓名 张泽欣 学号 指导教师 职称 时 间 目录 摘要 ...

  6. 信息论霍夫曼编码c语言,霍夫曼编码

    <信息论与编码>课程实验报告 姓 名 学 号 单 位 专 业 2014 年 12 月 4 日 实验一 一.实验目的 1.理解信源编码的意义: 2.掌握霍夫曼编码的方法及计算机实现: 二.实 ...

  7. 南邮哈夫曼编码c语言代码_漫画:“哈夫曼编码” 是什么鬼?

    ​在上一期,我们介绍了一种特殊的数据结构 "哈夫曼树",也被称为最优二叉树.没看过的小伙伴可以点击下方链接: 漫画:什么是 "哈夫曼树" ? 那么,这种数据结构 ...

  8. c语言用赫夫曼编码压缩文件,用哈夫曼编码C语言实现文件压缩

    用哈夫曼编码实现文件压缩,C语言编写,简单实用, if(j%8!=0) /*按八位读取*/ { for(f=j%8;f<8;f++) strcat(header[i].bits,"0& ...

  9. 哈夫曼编码c语言实现

    哈夫曼编码的原理看 百度百科 先生成一个哈夫曼树,参考 哈夫曼树c语言实现 生成接近等长码 须要注意的是,为了缩短码长方差,且编出的码更接近于等长码,排序的时候,第一优先级为概率大小,第二优先级为左节 ...

最新文章

  1. 与癌症作战的 12 家 AI 公司,八仙过海各显神通
  2. android log丢失(二)使用and4.4log kernel机制
  3. Excel是计算机应用软件,计算机应用软件Excel 2003教学计划
  4. saleor的生产环境部署-失败记录
  5. bind()的实现(持续更新中)
  6. 最近邻插值算法 python实现
  7. VC++开发学习一(MFC中的CString类的常用的方法技巧介绍)
  8. easyui combobox根据输入内容动态查找_制作智能下拉菜单,自动筛选想要输入的数据,同事都看呆了...
  9. 凯撒密码matlab转换,教你如何简单使用凯撒密码 详细始末
  10. 5款优秀的在线表单设计器
  11. Java安全生态-Java加解密API详解-Java安全框架官方文档翻译:对称/非对称加密、数字签名、数字证书、安全通信、密钥库等
  12. python验证身份证号码大全_对身份证号码查重,你经常用的方法是错误,这个才是正确的方法...
  13. 教师资格证考试攻略(高中信息技术)
  14. 计算机键盘上删除,电脑键盘删除键是哪一个
  15. Adobe Experience Cloud落地中国,Adobe、微软与世纪互联共庆三方合作
  16. FPGA开平方的实现(三种方法)
  17. layui之table操作点击编辑,使用layer.open回显值
  18. [移动GIS] 4.1-TDOA定位
  19. 3500字干货!精准解决3大难题,助力服装行业数字化转型
  20. .NET NAudio音频录制方法 2021-02-13

热门文章

  1. Python读excel去重
  2. ABAP WORKFLOW工作流创建(一)
  3. 8.词袋和词向量模型
  4. 单相桥式有源逆变电路matlab,单相桥式有源逆变电路在MATLAB中的建模与仿真
  5. TUTK[MediaSDK][Android] 如何在android系统上实现后台编码功能
  6. 数字化与高质量发展之百年大变局下的科技创新--学习王坚院士讲座的记录和读后感
  7. 解决 Arch/Manjaro Linux AUR 的 MindMaster 无法输入中文和登录的问题
  8. VsCode 安装 SonarLint 步骤
  9. 实现一个小轮子:用AOP实现异步上传
  10. 知云文献翻译安装教程_阅读英文文献的好帮手