数据结构(15)–哈夫曼树以及哈夫曼编码的实现

数据结构(15)–哈夫曼树以及哈夫曼编码的实现参考书籍 数据结构 C 语言版 严蔚敏吴伟民编著清华大学出版社本文中的代码可从这里下载 https github com qingyujean data structure1 哈夫曼树假设有 n 个权值 w1 w2 wn 试构造一棵含有 n 个叶子结点的二叉树 每个叶子节点带权威 wi 则其中带权路径长度 WPL 最小的二叉树叫做最优二叉树或者哈夫曼树 特点 哈

参考书籍:数据结构(C语言版)严蔚敏吴伟民编著清华大学出版社

本文中的代码可从这里下载:https://github.com/qingyujean/data-structure

1.哈夫曼树

    假设有n个权值{w1, w2, …, wn},试构造一棵含有n个叶子结点的二叉树,每个叶子节点带权为wi,则其中带权路径长度WPL最小的二叉树叫做最优二叉树或者哈夫曼树

    特点:哈夫曼树中没有度为1的结点,故由n0 = n2+1以及m= n0+n1+n2,n1=0可推出m=2*n0-1,即一棵有n个叶子节点的哈夫曼树共有2n-1个节点。

2.哈夫曼编码

    通信传送的目标是使总码长尽可能的短。

    变长编码的原则:
    1.使用频率高的字符用尽可能短的编码(这样可以减少数据传输量);
    2.任一字符的编码都不能作为另一个字符编码的开始部分(这样就使得在两个字符的编码之间不需要添加分隔符号)。这种编码称为前缀编码




    根据每种字符在电文中出现的次数构造哈夫曼树,将哈夫曼树中每个分支结点的左分支标上0,右分支标上1,把从根结点到每个叶子结点的路径上的标号连接起来,作为叶结点所代表的字符的编码。这样得到的编码称为哈夫曼编码

    思考为什么哈夫曼编码符合变长编码的原则?哈夫曼树所构造出的编码的长度是不是最短的?

     哈夫曼树求得编码为最优前缀码的原因: 在构造哈夫曼树的过程中:

3.哈夫曼编码实例

四种字符以及他们的权值:a:30, b:5, c:10, d:20

第一步:构建哈夫曼树

数据结构(15)--哈夫曼树以及哈夫曼编码的实现

第二步:为哈夫曼树的每一条边编码

数据结构(15)--哈夫曼树以及哈夫曼编码的实现

第三步:生成哈夫曼编码表

数据结构(15)--哈夫曼树以及哈夫曼编码的实现

4.代码实现

4.1哈夫曼树定义

哈夫曼树的存储结构:采用静态三叉链表

#include 
  
    #include 
   
     #include 
    
      #define N 4//带权值的叶子节点数或者是需要编码的字符数 #define M 2*N-1//n个叶子节点构造的哈夫曼树有2n-1个结点 #define MAX 10000 typedef char TElemType; //静态三叉链表存储结构 typedef struct{ //TElemType data; unsigned int weight;//权值只能是正数 int parent; int lchild; int rchild; }HTNode;//, *HuffmanTree; typedef HTNode HuffmanTree[M+1];//0号单元不使用 typedef char * HuffmanCode[N+1];//存储每个字符的哈夫曼编码表,是一个字符指针数组,每个数组元素是指向字符指针的指针 
     
    
  

 

4.2构造哈夫曼树

//构造哈夫曼树 void createHuffmanTree(HuffmanTree &HT, int *w, int n){ if(n <= 1) return; //对树赋初值 for(int i = 1; i <= n; i++){//HT前n个分量存储叶子节点,他们均带有权值 HT[i].weight = w[i]; HT[i].lchild = 0; HT[i].parent = 0; HT[i].rchild = 0; } for(int i=n+1; i <=M; i++){//HT后m-n个分量存储中间结点,最后一个分量显然是整棵树的根节点 HT[i].weight = 0; HT[i].lchild = 0; HT[i].parent = 0; HT[i].rchild = 0; } //开始构建哈夫曼树,即创建HT的后m-n个结点的过程,直至创建出根节点。用哈夫曼算法 for(int i = n+1; i <= M; i++){ int s1, s2; select(HT, i-1, s1, s2);//在HT[1...i-1]里选择parent为0的且权值最小的2结点,其序号分别为s1,s2,parent不为0说明该结点已经参与构造了,故不许再考虑 HT[s1].parent = i; HT[s2].parent = i; HT[i].lchild = s1; HT[i].rchild = s2; HT[i].weight = HT[s1].weight + HT[s2].weight; } }

 

//在HT[1...k]里选择parent为0的且权值最小的2结点,其序号分别为s1,s2,parent不为0说明该结点已经参与构造了,故不许再考虑 void select(HuffmanTree HT, int k, int &s1, int &s2){ //假设s1对应的权值总是<=s2对应的权值 unsigned int tmp = MAX, tmpi = 0; for(int i = 1; i <= k; i++){ if(!HT[i].parent){//parent必须为0 if(tmp > HT[i].weight){ tmp = HT[i].weight;//tmp最后为最小的weight tmpi = i; } } } s1 = tmpi; tmp = MAX; tmpi = 0; for(int i = 1; i <= k; i++){ if((!HT[i].parent) && i!=s1){//parent为0 if(tmp > HT[i].weight){ tmp = HT[i].weight; tmpi = i; } } } s2 = tmpi; }

 

打印哈夫曼树

//打印哈夫曼满树 void printHuffmanTree(HuffmanTree HT, char ch[]){ printf("\n"); printf("data, 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"); }

 

4.3编码

为哈夫曼树的每一条分支编码,并生成哈夫曼编码表HC

//为每个字符求解哈夫曼编码,从叶子到根逆向求解每个字符的哈夫曼编码 void encodingHuffmanCode(HuffmanTree HT, HuffmanCode &HC){ //char *tmp = (char *)malloc(n * sizeof(char));//将每一个字符对应的编码放在临时工作空间tmp里,每个字符的编码长度不会超过n char tmp[N]; tmp[N-1] = '\0';//编码的结束符 int start, c, f; for(int i = 1; i <= N; i++){//对于第i个待编码字符即第i个带权值的叶子节点 start = N-1;//编码生成以后,start将指向编码的起始位置 c = i; f = HT[i].parent; while(f){//f!=0,即f不是根节点的父节点 if(HT[f].lchild == c){ tmp[--start] = '0'; }else{//HT[f].rchild == c,注意:由于哈夫曼树中只存在叶子节点和度为2的节点,所以除开叶子节点,节点一定有左右2个分支 tmp[--start] = '1'; } c = f; f = HT[f].parent; } HC[i] = (char *)malloc((N-start)*sizeof(char));//每次tmp的后n-start个位置有编码存在 strcpy(HC[i], &tmp[start]);//将tmp的后n-start个元素分给H[i]指向的的字符串 } }

打印哈夫曼编码表,当编码表生成以后,以后就可以对字符串进行编码了,只要对应编码表进行转换即可

//打印哈夫曼编码表 void printHuffmanCoding(HuffmanCode HC, char ch[]){ printf("\n"); for(int i = 1; i <= N; i++){ printf("%c:%s\n", ch[i], HC[i]); } printf("\n"); }

 

4.4解码

//解码过程:从哈夫曼树的根节点出发,按字符'0'或'1'确定找其左孩子或右孩子,直至找到叶子节点即可,便求得该字串相应的字符 void decodingHuffmanCode(HuffmanTree HT, char *ch, char testDecodingStr[], int len, char *result){ int p = M;//HT的最后一个节点是根节点,前n个节点是叶子节点 int i = 0;//指示测试串中的第i个字符 //char result[30];//存储解码以后的字符串 int j = 0;//指示结果串中的第j个字符 while(i 
  

 

4.5演示

int main(){ HuffmanTree HT; TElemType ch[N+1];//0号单元不使用,存储n个等待编码的字符 int w[N+1];//0号单元不使用,存储n个字符对应的权值 printf("请输入%d个字符以及该字符对应的权值(如:a,20):\n", N); for(int i = 1; i <= N; i++){ scanf("%c,%d", &ch[i], &w[i]); getchar();//吃掉换行符 }//即w里第i个权值对应的是ch里第i个字符元素 createHuffmanTree(HT, w , N);//构建哈夫曼树 printHuffmanTree(HT, ch); HuffmanCode HC;//HC有n个元素,每个元素是一个指向字符串的指针,即每个元素是一个char *的变量 encodingHuffmanCode(HT, HC);//为每个字符求解哈夫曼编码 printHuffmanCoding(HC, ch); //解码测试用例:abaccda----010 char * testDecodingStr = "010"; int testDecodingStrLen = 14; printf("编码%s对应的字符串是:", testDecodingStr); char result[30];//存储解码以后的字符串 decodingHuffmanCode(HT, ch, testDecodingStr, testDecodingStrLen, result);//解码(译码),通过一段给定的编码翻译成对应的字符串 printf("%s\n", result); return 0; }

 

本文中的代码可从这里下载:https://github.com/qingyujean/data-structure

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227317.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午9:34
下一篇 2026年3月16日 下午9:35


相关推荐

  • python调用c++动态库_python登陆mt4

    python调用c++动态库_python登陆mt4广告关闭腾讯云11.11云上盛惠,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!运行平台:windowspython版本:python3.6ide:sublimetext其他工具:chrome浏览器0、写在前面的话本文是基于基础版上做的修改,如果没有阅读基础版,请移步python爬虫抓取智联招聘(基础版)在基础版中,构造url时使用了urllib…

    2022年8月15日
    11
  • linux使用ps命令查看和控制进程_使用ps命令得到当前进程

    linux使用ps命令查看和控制进程_使用ps命令得到当前进程ps命令Linuxps(英文全拼:processstatus)命令用于显示当前进程的状态,类似于windows的任务管理器查看所有进程ps-A显示所有进程信息,连同命令行ps-

    2022年7月30日
    10
  • ping命令详解 ping命令入门详解

    ping命令详解 ping命令入门详解在这个时代,科技越来越发达,网络已经越来越成为人们不可缺少的一部分。计算机也已经是很多学校的课程了,因为计算机技术是非常有技术性的专业,它其中涉及到很多专业知识,需要通过学习才能掌握。今日小编就为大家介绍一个计算机的命令,它叫做Ping,这边介绍一下它的入门知识,主要是关于ping连接和命令方面的介绍。  1、Ping的基础知识  ping命令相信大家已经再熟悉不过了,但是能把ping的功能发…

    2026年2月18日
    9
  • windows 7 开机错误 未能连接到一个Windows服务

    windows 7 开机错误 未能连接到一个Windows服务Windows无法连接到systemeventnotificationservice服务。此问题阻止标准用户登录系统。作为管理员用户,您可以复查系统事件日志,以获得有关此服务未响应原因的详细信息。如下图:莫名其妙突然电脑死机了,重启后就出现了上述问题,并且电脑无法连网,开机速度慢到需要4分钟多,刚开机还黑屏。查看systemeventnotificationserv…

    2022年5月15日
    46
  • keil4注册机注册不了怎么办?我已经试过下面的注册机不行。求各大神指教一下?

    keil4注册机注册不了怎么办?我已经试过下面的注册机不行。求各大神指教一下?

    2022年5月13日
    58
  • linux中wq(linux a)

    LinuxESC:wq和:wq!的区别LinuxESC:wq和:wq!的区别发布者:IT人在线|发表时间:2018-12-417:20:43LinuxESC:wqesc(键退出)->:(符号输入)->wq(保存退出)wq(存盘并退出write%quite)即使文件没有被修改也强制写入,并更新文件的修改时间。:wq和:wq!的区别::wq(保存编辑操作退出)强…

    2022年4月11日
    120

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号