图文详解JAVA实现哈夫曼树

前言 

我想学过数据结构的小伙伴一定都认识哈夫曼,这位大神发明了大名鼎鼎的“最优二叉树”,为了纪念他呢,我们称之为“哈夫曼树”。哈夫曼树可以用于哈夫曼编码,编码的话学问可就大了,比如用于压缩,用于密码学等。今天一起来看看哈夫曼树到底是什么小编。 

概念

当然,套路之一,首先我们要了解一些基本概念。 

      1、路径长度:从树中的一个结点到另一个结点之间的分支构成这两个结点的路径,路径上的分支数目称为路径长度。

      2、树的路径长度:从树根到每一个结点的路径长度之和,我们所说的完全二叉树就是这种路径长度最短的二叉树。

      3、树的带权路径长度:如果在树的每一个叶子结点上赋上一个权值,那么树的带权路径长度就等于根结点到所有叶子结点的路径长度与叶子结点权值乘积的总和。 

那么我们怎么判断一棵树是否为最优二叉树呢,先看看下面几棵树:

 

他们的带权长度分别为:

     WPL1:7*2+5*2+2*2+4*2=36

     WPL2:7*3+5*3+2*1+4*2=46

     WPL3:7*1+5*2+2*3+4*3=35

很明显,第三棵树的带权路径最短(不信的小伙伴可以试一试,要是能找到更短的,估计能拿图灵奖了),这就是我们所说的“最优二叉树(哈夫曼树)”,它的构建方法很简单,依次选取权值最小的结点放在树的底部,将最小的两个连接构成一个新结点,需要注意的是构成的新结点的权值应该等于这两个结点的权值之和,然后要把这个新结点放回我们需要构成树的结点中继续进行排序,这样构成的哈夫曼树,所有的存储有信息的结点都在叶子结点上。

概念讲完,可能有点小伙伴还是“不明觉厉”。

下面举个例子构建一下就清楚了。

有一个字符串:aaaaaaaaaabbbbbaaaaaccccccccddddddfff

第一步,我们先统计各个字符出现的次数,称之为该字符的权值。a 15,b 5,c 8,d 6,f 3。

第二步,找去这里面权值最小的两个字符,b5和f3,构建节点。

 

然后将f3和b5去掉,现在是a15,c8,d6,fb8。

第三步,重复第二步,直到构建出只剩一个节点。

  

现在是dfb14,a15,c8。

 

最后,

 

ok,这样我们的哈夫曼树就构造完成了。 

构建的步骤 

按照上面的逻辑,总结起来,就是一下几个步骤:

     1.统计字符串中字符以及字符的出现次数;

     2.根据第一步的结构,创建节点;

     3.对节点权值升序排序;

     4.取出权值最小的两个节点,生成一个新的父节点;

     5.删除权值最小的两个节点,将父节点存放到列表中;

     6.重复第四五步,直到剩下一个节点;

     7.将最后的一个节点赋给根节点。 

java代码

原理说完了,接下来是代码实现了。

首先需要有个节点类来存放数据。

package huffman;
/**
 * 节点类
 * @author yuxiu
 *
 */
public class Node {
 public String code;// 节点的哈夫曼编码
 public int codeSize;// 节点哈夫曼编码的长度
 public String data;// 节点的数据
 public int count;// 节点的权值
 public Node lChild;
 public Node rChild;

 public Node() {
 }

 public Node(String data,int count) {
  this.data = data;
  this.count = count;
 }

 public Node(int count,Node lChild,Node rChild) {
  this.count = count;
  this.lChild = lChild;
  this.rChild = rChild;
 }

 public Node(String data,int count,Node rChild) {
  this.data = data;
  this.count = count;
  this.lChild = lChild;
  this.rChild = rChild;
 }
}

然后就是实现的过程了。

package huffman;

import java.io.*;
import java.util.*;

public class Huffman {
 private String str;// 最初用于压缩的字符串
 private String newStr = "";// 哈夫曼编码连接成的字符串 
 private Node root;// 哈夫曼二叉树的根节点
 private boolean flag;// 最新的字符是否已经存在的标签
 private ArrayList<String> charList;// 存储不同字符的队列 相同字符存在同一位置
 private ArrayList<Node> NodeList;// 存储节点的队列
 
  15  16  /**
  * 构建哈夫曼树
  * 
  * @param str
  */
 public void creatHfmTree(String str) {
  this.str = str;
  charList = new ArrayList<String>();
  NodeList = new ArrayList<Node>();
  // 1.统计字符串中字符以及字符的出现次数
  // 基本思想是将一段无序的字符串如ababccdebed放到charList里,分别为aa,bbb,cc,dd,ee
  // 并且列表中字符串的长度就是对应的权值
  for (int i = 0; i < str.length(); i++) {
   char ch = str.charAt(i); // 从给定的字符串中取出字符
   flag = true;
   for (int j = 0; j < charList.size(); j++) {
    if (charList.get(j).charAt(0) == ch) {// 如果找到了同一字符
     String s = charList.get(j) + ch;
     charList.set(j,s);
     flag = false;
     break;
    }
   }
   if (flag) {
    charList.add(charList.size(),ch + "");
   }
  }
  // 2.根据第一步的结构,创建节点
  for (int i = 0; i < charList.size(); i++) {
   String data = charList.get(i).charAt(0) + ""; // 获取charList中每段字符串的首个字符
   int count = charList.get(i).length(); // 列表中字符串的长度就是对应的权值
   Node node = new Node(data,count); // 创建节点对象
   NodeList.add(i,node); // 加入到节点队列
  }

  // 3.对节点权值升序排序
  Sort(NodeList);
  while (NodeList.size() > 1) {// 当节点数目大于一时
   // 4.取出权值最小的两个节点,生成一个新的父节点
   // 5.删除权值最小的两个节点,将父节点存放到列表中
   Node left = NodeList.remove(0);
   Node right = NodeList.remove(0);
   int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和
   Node parent = new Node(parentWeight,left,right);
   NodeList.add(0,parent); // 将父节点置于首位

  }
  // 6.重复第四五步,就是那个while循环
  // 7.将最后的一个节点赋给根节点
  root = NodeList.get(0);
 }
 /**
  * 升序排序
  * 
  * @param nodelist
  */
 public void Sort(ArrayList<Node> nodelist) {
  for (int i = 0; i < nodelist.size() - 1; i++) {
   for (int j = i + 1; j < nodelist.size(); j++) {
    Node temp;
    if (nodelist.get(i).count > nodelist.get(j).count) {
     temp = nodelist.get(i);
     nodelist.set(i,nodelist.get(j));
     nodelist.set(j,temp);
    }

   }
  }

 }

 /**
  * 遍历
  * 
  * @param node
  *   节点
  */
 public void output(Node node) {
  if (node.lChild != null) {
   output(node.lChild);
  }
  System.out.print(node.count + " "); // 中序遍历
  if (node.rChild != null) {
   output(node.rChild);
  }
 }

 public void output() {
  output(root);
 }
/**
  * 主方法
  * 
  * @param args
  */
 public static void main(String[] args) {
  Huffman huff = new Huffman();//创建哈弗曼对象
  huff.creatHfmTree("sdfassvvdfgsfdfsdfs");//构造树
 }

总结

以上就是基于JAVA实现哈夫曼树的全部内容,希望这篇文章对大家学习使用JAVA能有所帮助。如果有疑问可以留言讨论。

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

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340