数据结构(哈夫曼树,哈夫曼编码)入门篇,JAVA实现

什么是哈夫曼树

哈夫曼树就是一种最优判定树,举个例子,如下一个判断逻辑
if(s<60) g=1;
else if(s<70) g=2
else if(s<80) g=3
else if(s<90) g=4
else g=5;
分数概率图如下
在这里插入图片描述

如果按照代码从上到下顺序构造判定树,那么如下图所示,如果有1000个数据需要判定,那么需要比较3150次
在这里插入图片描述

其实我们可以把概率大的区间放在树的高处,概率小的,放在树的低处,如下图的判定数,1000个数据只要比较2700次。
在这里插入图片描述
带权路径长度之和:根节点到各节点的路径长度与该结点上的权重的乘积
将上面的成绩区间的概率抽象为叶子节点的权重,比较次数抽象为根到各节点的路径,总比较次数就是带权路径长度之和,很容易看出把权重大的叶节点放在树的高处,权重小的放在低处,这样我们得到的带权路径长度之和就是最小的;
而哈夫曼树的定义正是要构造出一颗带权路径长度之和最小的二叉树。

哈夫曼树的构造可以采取从数组里面取最小值,但是这样做的话,每次插入元素到数组中,因为要维持升序,它会花费很多时间在维护上,时间复杂度为O(N)。于是我们可以采用最小堆,堆的插入操作时间复杂度为O(logN),比起数组还是快很多的。
哈夫曼树的总体时间复杂度为:创建最小堆(O(N))+插入N-1个数进入堆中(O(NlogN))+从堆中要删除2N-1个结点(O(NlogN))=O(Nlog(N))

哈夫曼编码

等长的编码
哈夫曼编码常用于文件压缩。英文ASCALL字符有一百多个,可以用7位来表示这些字符,再加上以为校验码,就是一个字节表示一个字符,i am amy,这段英文需要48位来表示。但是我们发现,只有i,a,y是不同的,加上am,我们只需要2位二进制就能表示一个字符,00–i,01–y,10–am。这样文件大小就被压缩到了6位。但是显然这样的等长的编码压缩是有局限的,一段英文中出现互不相同的字符是有限的,要想获得更好压缩效果,就需要变长的编码
变长的编码
让出现频率高的字符的编码短些,让出现频率低的字符的编码长一些,假设一段文本中,各字符出现频率如下,以及附上它的变长编码,显然如果用等长编码压缩这个文件,那么大小肯定是远超出变长编码

在这里插入图片描述

变长编码也有一个可以解决的弊端,那就是一个字符的编码不能是另一个编码的前缀,比如把e的编码改为00,那么在解析这串二进制时1011010001,这时解析到0001就会产生二义性,0001代表t,但从左往右解析时,解析到00发现是e,然后01找不到。所以一个编码不能是另一个编码的前缀,这里的e(00)就成为了a,s,t,nl的前缀了,彻底乱套了。
解决方法就是利用哈夫曼树,左分支记为0,右分支记为1,哈夫曼树是二叉树,而我们把叶结点的度改为字符,由于根节点到各个叶子节点的路径是不同的,可以解决前缀问题,再加上,这也符合哈夫曼树的一个特征,尽量把频率高的结点放在树的高处(编码自然也变短了,文件长度也得到压缩)。这简直就是完美匹配啊。
在这里插入图片描述

JAVA代码实现

最小堆(传入类型必须实现了Comparable接口,之后比较大小会调用其compareto方法,并且其比较器的规则是大于返回1,小于返回返回-1。

//必须传入一个Comparable的实现类,因为后续会用到类的内部比较器
public class Heap<E extends Comparable> {Comparable<? super E>[] list;//堆--存储Comparable的实现类int size;  //堆长度int capacity;//堆容量public Heap(int capacity){this.capacity=capacity;size=0;list=new Comparable[capacity+1];}//初始化,两种方式public void Init(E value,int index){if(index>0){ list[index]= value;size++;}elsenew RuntimeException("下标越界");}public void Init(E[] list){//一般数组下标从0开始,但最小堆里,下标0这个位置不用for(int i=0;i<list.length;i++)this.list[i+1]=list[i];size=list.length;}//创建最小堆public  void Build_Min_Heap(){for(int i=size/2;i>0;i--) {//从倒数第二层开始调整最小堆int child = 0;int parent = i;E par_X = (E) list[parent];for (; parent * 2 <= size; parent = child) {child = parent * 2;if (child + 1 <= size && list[child].compareTo((E) list[child + 1]) == 1)child++;if (par_X.compareTo((E) list[child]) == -1)break;list[parent] = list[child];}list[parent]=par_X;}}//最小堆插入public void Min_Insert(E node){list[++size]=node;for(int i=size;i/2>=0;i=i/2){if(i==1 || list[i/2].compareTo((E)node)==-1){list[i]=node;break;}else{list[i]=list[i/2];}}}//最小堆删除public E Min_Heap_Delete(){Comparable DeleteX=list[1];Comparable X=list[size--];int child=1;int parent=1;for(;parent*2<=size;parent=child){child=parent*2;if(child+1<=size && list[child].compareTo((E)list[child+1])==1 )child++;if(X.compareTo((E)list[child])==1)list[parent]=list[child];elsebreak;}list[parent]=X;return (E)DeleteX;}

哈夫曼树实现

//结点类型
class TreeNode implements Comparable{int value;String code;TreeNode left;TreeNode right;public TreeNode(int value){this.value=value;}@Overridepublic int compareTo(Object o) {TreeNode tn=(TreeNode) o ;if(value>tn.value)return 1;else if (value<tn.value)return -1;else return 0;}
}//哈夫曼树
import java.security.PublicKey;public class Hfman {TreeNode root;//初始化哈夫曼树public void Init(TreeNode[] T){TreeNode r=null;Heap<TreeNode> heap = new Heap<TreeNode>(T.length);heap.Init(T);heap.Build_Min_Heap();for(int i=1;i<=T.length-1;i++){//合并N-1次TreeNode a =heap.Min_Heap_Delete();TreeNode b =heap.Min_Heap_Delete();r = new TreeNode(a.value+b.value);r.left=a;r.right=b;heap.Min_Insert(r);}root=heap.Min_Heap_Delete();//堆中仅存最后一个结点就是根节点}//哈夫曼编码public static String HfmanCode(TreeNode node,String str){if(node!=null) {if (node.left != null && node.right != null) {HfmanCode(node.left,str+"0");HfmanCode(node.right,str+"1");}else  //叶子节点node.code=str;}return "";}//遍历哈夫曼树public static void InOder(TreeNode node){if(node!=null){InOder(node.left);System.out.println(node.value+":"+node.code);InOder(node.right);}}//测试public static void main(String[] args) {TreeNode[] nodes = new TreeNode[5];for(int i=0;i<5;i++)//测试数据1,2,3,4,5nodes[i] = new TreeNode(i+1);Hfman hfman = new Hfman();hfman.Init(nodes);//初始化构建哈夫曼树Hfman.HfmanCode(hfman.root,new String());//构造哈夫曼编码Hfman.InOder(hfman.root);//先序遍历}}

参考文献(数据结构第二版,陈越编)

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

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

相关文章

C++ class实现十字链表存储的图(完整代码)

代码如下: #include <iostream> #include <queue> using namespace std; typedef int InfoType; typedef int VertexType; const int MaxVertexNum 30;class ArcNode {friend class OLGraph;friend class VertexNode; private:int tailvertex, headvertex;ArcNod…

hystrix隔离策略对比

hystrix隔离策略 zuul的隔离实现是基于hystrix实现的&#xff0c;hystrix支持线程池隔离和信号量的隔离 # 信号量隔离&#xff1a; it executes on the calling thread and concurrent requests are limited by the semaphore count --引自官网单每次调用线程&#xff0c;当…

中国速度之二神山建设(1):坚强的领导核心,“小团队大后台”组织结构 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩丰…

通过Java编写一个服务器理解动态Web,静态Web

静态Web 说到Servlet自然就要说到Web,Web分为静态Web和动态Web&#xff0c;之前我一直都傻傻分不清两者的区别&#xff0c;直到用JAVA编写了一个服务器后才明白二者的区别&#xff0c;所谓静态Web&#xff0c;实际上就是指&#xff0c;客户端要请求的资源文件&#xff0c;服务器…

Hystrix在网关Zuul使用中遇到问题

Hystrix在网关Zuul使用中遇到问题 Zuul默认隔离策略级别是信号量隔离&#xff0c;默认最大隔离信号量是100 信号量隔离和线程隔离的区别&#xff1a;https://blog.csdn.net/liaojiamin0102/article/details/94394956默认的设置如源码&#xff1a; //在ZuulProperties 类下游…

C++ 实现无向图的最小生成树Prim算法(附完整代码)

实现Prim算法&#xff0c;需设置两个辅助一维数组lowcost和closevertex。 其中lowcost用来保存集合V-U中各顶点与集合U中各顶点构成的边中具有最小权值的边的权值&#xff1b;数组closevertex用来保存依附于该边的在集合U中的顶点。 过程: 假设初始状态时&#xff0c;U{u0}&a…

中国速度之二神山建设(2):完善的项目计划,高效能价值流 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末。&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩…

C++ 实现无向图的最小生成树Kruskal算法(完整代码)

按照Kruskal思想&#xff0c;n个结点的生成树有n-1条边&#xff0c;故反复上述过程&#xff0c;直到选取了n-1条边为止&#xff0c;就构成了一棵最小生成树。 实现Kruskal算法的关键问题是&#xff1a; 当一条边加入T的边集中后&#xff0c;如何判断是否构成回路。 一种解决方…

MySql 内连接,外连接查询方式区别

MySql 内连接&#xff0c;外连接查询方式 CREATE TABLE question_test (q_id int(11) DEFAULT NULL,q_name varchar(10) DEFAULT NULL,q_part varchar(10) DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8CREATE TABLE answer_test (a_id int(11) DEFAULT NULL,a_name varch…

让我的 .NET Core 博客系统支持 Docker

点击上方蓝字关注“汪宇杰博客”导语我的博客&#xff08;https://edi.wang&#xff09;所使用的博客系统 Moonglade 开源已经一年多了。目前已有至少4位社区朋友使用此系统在 Azure、阿里云上部署了自己的博客。可惜长久以来该系统一直缺乏 Docker 支持&#xff0c;而 .NET Co…

C++ 实现带权有向图的单源点最短路径Dijkstra算法(完整代码)

首先&#xff0c;引进一个辅助向量D&#xff0c;它的每个分量D[i]表示当前所找到的从始点v0到每个终点vi的最短路径的长度。 它的初态为&#xff1a;若从v0到vi有弧&#xff0c;则D[i]为弧上的权值&#xff1b;否则&#xff0c;置D[i]为∞。 显然&#xff0c;长度为 D[j]Min{…

Eureka与Zookeeper

CAP理论 C(Consistency)&#xff1a;一致性 A(Avaliability)&#xff1a;高可用 P(Partition tolerance)&#xff1a;分区容错性 Eureka & Zookeeper Eureka强调的是AP&#xff0c;即高可用&#xff0c;分区容错性Zookeeper强调的是CP&#xff0c;即一致性&#xff0c…

[ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?

昨天有个朋友在微信上问我一个问题&#xff1a;他希望通过动态脚本的形式实现对ASP.NET Core MVC应用的扩展&#xff0c;比如在程序运行过程中上传一段C#脚本将其中定义的Controller类型注册到应用中&#xff0c;问我是否有好解决方案。这是一个挺有意思的问题&#xff0c;我们…

C++ 实现带权有向图的每对顶点之间的最短路径Floyd算法(完整代码)

基本思想是&#xff1a; 假设求从顶点vi到vj的最短路径。 如果从vi到vj有弧&#xff0c;则从vi到vj存在一条长度为arcs[i][j]的路径&#xff0c;该路径不一定是最短路径&#xff0c;尚需进行n次试探。 首先考虑路径&#xff08;vi, v0, vj&#xff09;是否存在&#xff08;判别…

等额本息,等额本金区别

等额本金&#xff0c;等额本息区别 买房银行贷款分为两种&#xff1a; 等额本金和等额本息 等额本息 等额本息定义&#xff1a;还款本金占比逐月递增&#xff0c;利息占比逐月递减&#xff0c;月还款数不变由于每月的还款额度是一样的&#xff0c;其中每个月的还款包括了根…

【视频回放与课件】Build your AI solution with MLOps

4月8日在Global AI Community on Vitural Tour与大家分享了Build your AI solution with MLOps的专题&#xff0c;本课程主要介绍了微软Azure Machine Learning如何使用 , 以及如何通过Azure Machine Learning 结合 MLOps的概念完成机器学习项目的工作。本次Global AI Communit…

C++ 实现分块查找(顺序存储结构)(完整代码)

代码如下: #include <iostream> using namespace std; const int Maxsize 1000; const int MINNUM -999999; class Index_table {friend class SeqList; private:int key;int address; };class SeqList {//该顺序表从下标为0开始 public:~SeqList(){delete[] elem;del…

计算机网络知识扫盲

网络分层 主机网络层–数据链路层 主机网络层定义了一个特定的网络接口比如网卡或者wifi天线&#xff0c;如果通过物理连接向本地网络或者世界其他地方发送IP数据报。主机网络层中有链接不同计算机的硬件&#xff08;电缆&#xff0c;光纤&#xff0c;无线电波等&#xff09;…

《五分钟商学院》管理篇学习笔记

【商业知识】| 作者 / Edison Zhou这是恰童鞋骚年的第213篇原创文章在商业篇中&#xff0c;主要探讨的是企业如何处理与外部&#xff08;比如客户&#xff09;的关系。而在管理篇中&#xff0c;关注的重点则是企业如何处理与内部的关系。外部的世界很精彩&#xff0c;而内部的世…

如何在.NET应用程序中分析CPU使用率过高的问题

原文来自互联网&#xff0c;由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权&#xff0c;请联系小编&#xff0c;小编将在24小时内删除。限于译者的能力有限&#xff0c;个别语句翻译略显生硬&#xff0c;还请见谅。作者:胡安帕勃罗希达&#xff0c;JUAN PABLO SCIDA是一…