哈夫曼树与哈夫曼编码

一、哈夫曼树相关概念

        路径:从树中的一个节点到另一个节点之间的分支构成两个节点间的路径。

        节点的路径长度:两节点间路径的分支数(路径的个数)

        树的路径长度(TL):从根节点到树中每一个点的路径长度之和

路径长度:

        节点A到A,B,C,D,E,F的路径长度分别为0,1,1,2,2,2

 树的路径长度(TL):

        节点A到树中每个节点的路径之和 TL=0+1+1+2+2+2=8

        

        权(权重):将树中的节点赋值(具体实际意义与场合有关)。

        节点的带权路径长度:从根节点到该节点之间的路径长度与节点上权的乘积。

        树的带权路径长度(WPL):树中所有叶子节点的带权路径长度之和

        WPL=4*2+5*2+6*2=30

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

二、哈夫曼树构造方法

        1.n个给定权值的节点构成n棵树(1个森林),每个树都有且仅包含一个节点(不重复)

        2.选择两棵最小节点权值的树结合构成一棵新的二叉树,并且新二叉树的根节点的权值为其左右子树上根节点的权值之和.

        3.在森林中删除这两棵树,并且将新二叉树加入森林

        4.重复2-3操作,直至森林中仅剩一棵树为止,为哈夫曼树

         根据哈夫曼树的构建可以得出:

        1.初始时有n棵二叉树,经过n-1次合并成为哈夫曼树

        2.n-1次合并产生n-1个新节点,新节点都是具有两个孩子的分支节点

        哈夫曼树中共有n+n-1 =2n-1个结点,且其所有的分支结点的度均不为1。

三、代码实现哈夫曼树 

        构建哈夫曼树:需要每次根据各个节点的权重值,筛选出其中最小且无父的两个节点,然后构建二叉树                                              查找权值最小的两个节点:从数组起始位置开始,首先找到两个无父的节点(还未与其他树结合构建),然后和后续无父的节点作比较

        1.如果比两个节点中较小的那个还小,则保留这个节点,删除较大的节点

        2.如果介于两个权重值之间,替换较大的节点

查找权值最小节点的代码:

//HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
void Select(HuffmanTree HT, int x, int *s1, int *s2)
{int min1,min2;int i=1;//找到还没构建树的结点while(HT[i].parent!=0 && i<=x){i++;}min1=HT[i].weight;*s1=i;i++;while(HT[i].parent!=0 && i<=nx{i++;}//对找到的两个结点比较大小,min2为较大的,min1较为小的if(HT[i].weight<min1){min2=min1;*s2=*s1;min1=HT[i].weight;*s1=i;}else{min2=HT[i].weight;*s2=i;}for(int j=i+1;j<=nxj++){//如果有父结点,直接跳过,进行下一个if(HT[j].parent!=0){continue;}//如果比最小的还小if(HT[j].weight<min1){min2=min1;min1=HT[j].weight;*s2=*s1;*s1=j;}//如果介于两者之间else if(HT[j].weight>=min1 && HT[j].weight<min2){min2=HT[j].weight;*s2=j;}}
}

构建哈夫曼树代码:

//HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
void CreateHuffmanTree(HuffmanTree *HT,int *w,int n)
{int m=2*n-1; // m为哈夫曼树总节点数,n为叶子结点*HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); // 0号位置不用HuffmanTree p=*HT;// 初始化哈夫曼树中的所有结点for(int i=1;i<=n;i++){(p+i)->weight=*(w+i); // p[i].weight=w[i](p+i)->parent=0;(p+i)->left=0;(p+i)->right=0;}//从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点for(int i=n+1;i<=m;i++){(p+i)->weight=0;(p+i)->parent=0;(p+i)->left=0;(p+i)->right=0;}//构建哈夫曼树for(int i=n+1;i<=m;i++){int s1,s2;Select(*HT,i-1,&s1,&s2);(*HT)[s1].parent=(*HT)[s2].parent=i;  //新添第i个节点(*HT)[i].left=s1;(*HT)[i].right=s2;(*HT)[i].weight=(*HT)[s1].weight+(*HT)[s2].weight;}
}

四、哈夫曼编码(贪心思想)      

   哈夫曼编码也翻译为赫夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式, 属于一种程序算法,赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。 赫夫曼编码广泛地用于数据文件压缩。其压缩率通常在20%~90%之间赫夫曼码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,称之为最佳编码

        在远程通讯中,将待传字符转化成二进制的字符串。

        1.在通信领域中信息的其他处理方式

        ①定长编码:字符通过ASCII代码转化,再用二进制表示

        将 i like like like java do you like a java 定长编码       // 共40个字符(包括空格)  

        105 32 108 105 107 101 32 108 105 107 101 32 108 105 107 101 32 106 97 118 97 32 100 111 32 121 111 117 32 108 105 107 101 32 97 32 106 97 118 97  //对应Ascii码

        01101001 00100000 01101100 01101001 01101011 01100101 00100000 01101100 01101001 01101011 01100101 00100000 01101100 01101001 01101011 01100101 00100000 01101010 01100001 01110110 01100001 00100000 01100100 01101111 00100000 01111001 01101111 01110101 00100000 01101100 01101001 01101011 01100101 00100000 01100001 00100000 01101010 01100001 01110110 01100001 //对应的二进制 按照二进制来传递信息,总的长度是  359   (包括空格)

        定长编码的方法保证了信息传递的正确性,但是从上述过程可以看出,效率并不是最优的

        ②变长编码:将各个字符按照出现的次数进行编码,即出现次数越多的,编码越小.(出现次数越多的尽可能使其编码越小)

       

通过上述图例,可以很明显看出存在编码多义,即一段编码可以得出多种结果,显然是错误的方法.

        若需要运用上述思想,可以作前缀编码(每个字符的编码都不能是其他字符编码的前缀),例如上述 A=0 与 B=00,A的编码就是B编码的前缀.而哈夫曼编码就是运用这一想法.

        2.哈夫曼编码

        ①统计每个字符在字符串中出现的次数(出现次数越多,要求编码越短

        ②利用哈夫曼树的特点,以每个字符出现的次数作为权值,权越大的叶子节点离根越近,构造哈夫曼树

        ③在哈夫曼树的左分支标上0,右分支标上1

        则某个节点的编码即为从根节点到该节点路径上的标号连接而成,这样保证了前缀编码,不出现歧义

        3.哈夫曼编码代码实现

        首先根据数据构建哈夫曼树(上述代码已讲),再通过遍历哈夫曼树找出字符对应的二进制编码.

        法一:从叶子节点一直找到根节点,逆向记录途中的标记.

        法二:从根节点出发,一直到叶子节点,记录途中经过的标记

//HT为哈夫曼树,HC为存储结点哈夫曼编码的二维动态数组,n为结点的个数
void HuffmanCoding(HuffmanTree HT, HuffmanCode *HC,int n)
{*HC=(HuffmanCode) malloc((n+1)*sizeof(char*));char *cd=(char *)malloc(n*sizeof(char)); //存放结点哈夫曼编码的字符串数组cd[n-1]='\0';//字符串结束符for(int i=1;i<=n;i++){//从叶子结点出发,得到的哈夫曼编码是逆序的,需要在字符串数组中逆序存放int start=n-1;//当前结点在数组中的位置int c=i;//当前结点的父结点在数组中的位置int j=HT[i].parent;// 一直寻找到根结点while(j!=0){// 如果该结点是父结点的左孩子则对应路径编码为0,否则为右孩子编码为1if(HT[j].left==c)cd[--start]='0';elsecd[--start]='1';//以父结点为孩子结点,继续朝树根的方向遍历c=j;j=HT[j].parent;}//跳出循环后,cd数组中从下标 start 开始,存放的就是该结点的哈夫曼编码(*HC)[i]=(char *)malloc((n-start)*sizeof(char));strcpy((*HC)[i],&cd[start]);}//使用malloc申请的cd动态数组需要手动释放free(cd);
}

学习博文

​​​​​​【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码_哈夫曼编码树-CSDN博客

哈夫曼编码详解-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/674978.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】HTTPS

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;Linux 目录 &#x1f449;&#x1f3fb;HTTPS协议概念&#x1f449;&#x1f3fb;加密为什么要进行加密 &#x1f449;&#x1f3fb;常见的加密方式对称加密…

鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断

关于中断部分系列篇将用三篇详细说明整个过程. 中断概念篇 中断概念很多&#xff0c;比如中断控制器&#xff0c;中断源&#xff0c;中断向量&#xff0c;中断共享&#xff0c;中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.用海公公打比方说明白中断各个概念…

【自然语言处理】seq2seq模型——机器翻译

seq2seq模型——机器翻译 1 任务目标 1.1 案例简介 seq2seq是神经机器翻译的主流框架&#xff0c;如今的商用机器翻译系统大多都基于其构建&#xff0c;在本案例中&#xff0c;我们将使用由NIST提供的中英文本数据训练一个简单的中英翻译系统&#xff0c;在实践中学习seq2se…

STM32——基础篇

技术笔记&#xff01; 一、初识STM32 1.1 ARM内核系列 A 系列&#xff1a;Application缩写。高性能应用&#xff0c;比如&#xff1a;手机、电脑、电视等。 R 系列&#xff1a;Real-time缩写。实时性强&#xff0c;汽车电子、军工、无线基带等。 M 系列&#xff1a;Microcont…

景源畅信:个人抖音小店怎么开通?

在数字时代的浪潮中&#xff0c;个体创业已不再是遥不可及的梦想。特别是随着短视频平台的崛起&#xff0c;抖音不仅成为人们娱乐消遣的新宠&#xff0c;更是众多创业者眼中的“新大陆”。你是否也曾憧憬过在抖音上开一家属于自己的小店?那么&#xff0c;如何开通个人抖音小店…

【MySQL | 第九篇】重新认识MySQL锁

文章目录 9.重新认识MySQL锁9.1MySQL锁概述9.2锁分类9.2.1锁的粒度9.2.2锁的区间9.2.3锁的性能9.2.4锁的级别 9.3拓展&#xff1a;意向锁9.3.1意向锁概述9.3.2意向锁分类9.3.3意向锁作用&#xff08;1&#xff09;意向锁的兼容互斥性&#xff08;2&#xff09;例子1&#xff08…

揭秘 IEEE/ACM Trans/CCF/SCI,谁才是科研界的王者?

会议之眼 快讯 在学术探索的浩瀚星海中&#xff0c;每一篇论文都像是一颗璀璨的星辰&#xff0c;而那些被顶级期刊或会议收录的论文&#xff0c;则无疑是最耀眼的几颗。 在众多评价标准中&#xff0c;IEEE/ACM Transactions、CCF推荐期刊和会议、SCI分区期刊&#xff0c;它们…

部署YUM仓库以及NFS共享服务

YUM仓库部署 一.YUM概述 YUM仓库源是一种软件包管理工具&#xff0c;用于在Linux系统上安装、更新和删除软件包。YUM仓库源包含了软件包的元数据信息和实际的软件包文件。用户可以通过配置YUM仓库源&#xff0c;从中下载和安装软件包。 常见的YUM仓库源包括&#xff1a; 本…

【数据结构】链表经典OJ题目练习(2)

面试题 02.02. 返回倒数第 k 个节点 - 力扣&#xff08;LeetCode&#xff09; 思路1&#xff1a;先计算出链表的长度&#xff0c;在将链表中的值存在数组中&#xff0c;在返回第k个节点。 思路2&#xff1a;利用快慢指针&#xff0c;先让快指针走k步&#xff0c;在让快慢指针分…

HSP_06章_Python_函数

文章目录 P67 函数入门1. 基本语法2. 函数的调用3. 函数的注意事项和使用细节 P71 递归机制1. 基本介绍2.递归能解决什么问题3. 递归的重要规则 P72 函数作为参数传递P73 lambda匿名函数P74 全局变量和局部变量 P67 函数入门 1. 基本语法 2. 函数的调用 3. 函数的注意事项和使…

Vulnhub项目:ICA: 1

1、靶机介绍 靶机地址&#xff1a;ICA: 1 ~ VulnHub 2、渗透过程 首先&#xff0c;部署好靶机后&#xff0c;进行探测&#xff0c;发现靶机ip和本机ip&#xff0c;靶机ip156&#xff0c;本机ip146。 然后查看靶机ip有哪些端口&#xff0c;nmap一下。 出现22、80、3306端口&a…

如何把Java的定时任务写到数据库里面去配置?

之前是这样写的&#xff0c;每次要改定时器都要修改发版&#xff0c;很麻烦&#xff1a; package cn.net.cdsz.ccb.common.scheduled;import cn.net.cdsz.ccb.business.config.Custom; import cn.net.cdsz.ccb.business.service.CCBBankService; import cn.net.cdsz.ccb.busin…