java实现哈夫曼编码

哈夫曼树

既然是学习哈夫曼编码,我们首先需要知道什么是哈夫曼树:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

哈夫曼编码

在日常计算机的使用中,我们一定会出现下面这种情况:假如给定a、b、c、d、e五个字符,它们在文本中出现的概率如下图所示:

字符

概率

a

0.12

b

0.40

c

0.15

d

0.05

e

0.25

我们现在要将文本编码成0/1序列从而使得计算机能够进行读取和计算。为了保证每个字符的独一性,所以我们给予不同的的字符以不同的编码。如果给每个字符赋予等长的编码的话,会使得平均的编码长度过长,影响计算时的性能,浪费计算机的资源(定长编码的缺点)。这时我们就想到了变长编码,理所当然的,给出现概率较大的字符赋予较短的编码,概率较小的字符赋予较长的编码,这样在计算的时候不就可以节省很多时间了吗?可这样我们又面临到了一个巨大的问题,我们来看下面这种情况,我们对字符进行编码:

字符

概率

编码

a

0.12

01

b

0.40

0

c

0.15

00

d

0.05

10

e

0.25

1

假设现在文本中的字符是bcd,转换之后的0/1序列为00010,可我们要在转换成文本的时候究竟是把第一位的0读作b还是把前两位的00读作c呢?为了解决这个问题,就又有了前缀码的概念。顾名思义,前缀码的含义就是任意字符的编码都不是其他字符编码的前缀。那么该如何形成前缀码呢?首先我们要构造一棵二叉树,指向左孩子的"边"记作0,指向右孩子的点记作“1”,叶子节点为代编码的字符,出现概率越大的字符离根的距离就越近。

字符

概率

编码

a

0.12

0100

b

0.40

1

c

0.15

0101

d

0.05

011

e

0.25

00

我们在前面提到:哈夫曼树的带权路径最小,所以有哈夫曼树构成的前缀码记作哈夫曼编码。哈夫曼作为已知的最佳无损压缩算法,满足前缀码的性质,可以即时解码。

哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。

java代码实现

实现哈夫曼编码的主要思路为从指定的文件中读出文本,首先通过遍历获得各个字符出现的概率,根据出现概率的大小构造二叉树,在此基础上进行编码解码。

首先来构造哈夫曼树的结点类,在结点类中有老生常谈的变量数据data、权重weight、指向左孩子的指针left、指向右孩子的指针right。除此之外,为了在构造了哈夫曼树之后容易对data进行编码,在这里我还使用了一个变量code来储存0/1字符。

public class Node implements Comparable> {

private T data;

private double weight;

private Node left;

private Node right;

String code;

public Node(T data, double weight){

this.data = data;

this.weight = weight;

this.code = "";

}

public T getData() {

return data;

}

public void setData(T data) {

this.data = data;

}

public double getWeight() {

return weight;

}

public void setWeight(double weight) {

this.weight = weight;

}

public Node getLeft() {

return left;

}

public void setLeft(Node left) {

this.left = left;

}

public Node getRight() {

return right;

}

public void setRight(Node right) {

this.right = right;

}

public String getCode(){

return code;

}

public void setCode(String str){

code = str;

}

@Override

public String toString(){

return "data:"+this.data+";weight:"+this.weight+";code: "+this.code;

}

@Override

public int compareTo(Node other) {

if(other.getWeight() > this.getWeight()){

return 1;

}

if(other.getWeight() < this.getWeight()){

return -1;

}

return 0;

}

}

紧接着我们来构建哈夫曼树。

createTree方法返回一个结点,也就是根结点。首先把所有的nodes结点类都储存在一个List中,利用Collections的sort方法把结点按照权值的大小按照从大到小的顺序进行排列。然后把List中的倒数第二个元素设为左孩子,倒数第一个元素设为右孩子。这个时候要注意:它们的双亲结点为以左右孩子的权值的和作为权值的构成的新的结点。然后删去左右孩子结点,将形成的新结点加入的List中。直到List中只剩下一个结点,也就是根结点时为止。

public Node createTree(List> nodes) {

while (nodes.size() > 1) {

Collections.sort(nodes);

Node left = nodes.get(nodes.size() - 2);

left.setCode(0 + "");

Node right = nodes.get(nodes.size() - 1);

right.setCode(1 + "");

Node parent = new Node(null, left.getWeight() + right.getWeight());

parent.setLeft(left);

parent.setRight(right);

nodes.remove(left);

nodes.remove(right);

nodes.add(parent);

}

return nodes.get(0);

}

在构建哈夫曼树的类中还实现了一个广度遍历的方法,在遍历的时候,每遍历到左孩子,就把结点中的code变量加上“0”,这里的加不是简单的数学运算,而是字符串的叠加。每遍历到右孩子,就把结点中的code变量加上“1”,这样遍历过一遍后,叶子结点中的code储存的就是对应的哈夫曼编码值。

public List> breadth(Node root) {

List> list = new ArrayList>();

Queue> queue = new ArrayDeque>();

if (root != null) {

queue.offer(root);

root.getLeft().setCode(root.getCode() + "0");

root.getRight().setCode(root.getCode() + "1");

}

while (!queue.isEmpty()) {

list.add(queue.peek());

Node node = queue.poll();

if (node.getLeft() != null)

node.getLeft().setCode(node.getCode() + "0");

if (node.getRight() != null)

node.getRight().setCode(node.getCode() + "1");

if (node.getLeft() != null) {

queue.offer(node.getLeft());

}

if (node.getRight() != null) {

queue.offer(node.getRight());

}

}

return list;

}

接下来我们首先从指定的文件中读取文本:

File file = new File("G:/usually/input/input1.txt");

这里我们还需要构造一个类对读取的文本进行处理:chars数组储存的是文本中所有可能的字符,number数组中储存的是文本中所有字符出现的次数,这里我所申请的number数组的长度为27,为什么是27呢?因为在这里加上了空格。在num方法中利用双循环对字符进行计数,在这里不再过多赘述。

public class readtxt {

char[] chars = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s'

,'t','u','v','w','x','y','z',' '};

int[] number = new int[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

public String txtString(File file){

StringBuilder result = new StringBuilder();

try{

BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件

String s = null;

while((s = br.readLine())!=null){//使用readLine方法,一次读一行

result.append(System.lineSeparator()+s);

num(s);

}

br.close();

}catch(Exception e){

e.printStackTrace();

}

return result.toString();

}

public void num(String string){

for(int i = 0;i<27;i++){

int temp = 0;

for(int j = 0;j

if(string.charAt(j) == chars[i])

temp++;

}

number[i] += temp;

}

}

public int[] getNumber(){

return number;

}

public char[] getChars(){

return chars;

}

}

调用上面readtxt中的方法对文本进行处理,定义两个数组获得文本中出现的字符和字符出现的次数。

readtxt read = new readtxt();

String temp = read.txtString(file);

int[] num = read.getNumber();

char[] chars = read.getChars();

利用一个循环把对应的data值和weight权重值构造成结点加入到list中。

for(int i = 0;i<27;i++){

System.out.print(chars[i]+":"+num[i]+" ");

list.add(new Node(chars[i]+"",num[i]));

}

构建哈夫曼树并得到根结点。

HuffmanTree huffmanTree = new HuffmanTree();

Node root = huffmanTree.createTree(list);

调用哈夫曼树中广度遍历方法,在建立两个新的list用来储存遍历之后的字符以及相对应的哈夫曼编码。

list2=huffmanTree.breadth(root);

for(int i = 0;i

if(list2.get(i).getData()!=null) {

list3.add(list2.get(i).getData());

list4.add(list2.get(i).getCode());

}

}

对从指定文本中读出的数据进行遍历,并与list3中的字符进行比较,如若相等,则转换为对应的变码。直至遍历结束,哈夫曼编码完成。

for(int i = 0;i

for(int j = 0;j

if(temp.charAt(i) == list3.get(j).charAt(0))

result += list4.get(j);

}

}

在解码的过程中我在这里选择了用一个list5把已编码完成的字符串分开来储存,然后对list5在进行遍历,从list5中取出一位元素temp2与list4中对应的编码进行比对,如果没有相同的,再从list5中读取一位加到temp2的后面,如果有,则清空temp2。每次找到相同的,就把对应的字符加到temp3的后面,直至整个list5遍历结束,temp3即为解码后的结果。

for(int i = 0;i

list5.add(result.charAt(i)+"");

}

while (list5.size()>0){

temp2 = temp2+"" +list5.get(0);

list5.remove(0);

for(int i=0;i

if (temp2.equals(list4.get(i))) {

temp3 = temp3+""+list3.get(i);

temp2 = "";

}

}

}

写入文件,大功告成!

File file2 =new File("G:/usually/input/input2.txt");

Writer out =new FileWriter(file2);

out.write(temp3);

out.close();

最后得到的结果:

java哈夫曼编码译码_java实现哈夫曼编码相关推荐

  1. 【编码译码】基于matlab QC-LDPC码编码和译码【含Matlab译码 2194期】

    ⛄一.获取代码方式 获取代码方式1: 完整代码已上传我的资源:[编码译码]基于matlab QC-LDPC码编码和译码[含Matlab译码 2194期] 点击上面蓝色字体,直接付费下载,即可. 获取代 ...

  2. java 编码 类型_java字符类型采用什么编码方式

    java 的字符类型采用的是 Unicode 编码方案. Java采用UTF-16编码作为内码,也就是说在JVM内部,文本是用16位码元序列表示的,常用的文本就是字符(char)和字符串(String ...

  3. 编码译码算法c语言程序,LDPC编码C代码 LDPC编译码的C代码程序 - 下载 - 搜珍网

    压缩包 : d29aa0dfee3e50fde4850fb5dc47a57a.rar 列表 LDPC编码C代码/LDPC编码C代码/Debug/ldpc_encode.exe LDPC编码C代码/LD ...

  4. java下载文件跳转页面_java servlet笔记:设置编码集、文件下载和两种服务器跳转-java下载文件...

    java笔记 一设置编码集: 设置请求的编码: request.setCharacterEncoding(服务器编码) 在代码中也就是这样: 本身这个语法是对请求实体进行设置编码,针对于post有效, ...

  5. 哈夫曼编码 译码java_基于Java的哈夫曼编码译码系统_报告毕业论文

    基于Java的哈夫曼编码译码系统_报告毕业论文 1课 程 设 计Java 与面向对象程序设计课程设计基于 Java 的哈夫曼编码译码系统1.问题描述和分工情况1.1 问题描述使用 Java 语言实现哈 ...

  6. java哈夫曼编码与译码_哈夫曼树与编码译码实现

    标签: 一.哈弗曼树的基本概念. 哈夫曼树,又称最优树,是一类带权路径长度最短的树.下面有几个概念: (1)路径. 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径. (2)路径长度. 路径 ...

  7. 哈夫曼树的java实现_java实现哈夫曼树

    哈夫曼译码,就是将输入的译码还原成对应的字符. 抽象的算法描述:将建立哈夫曼树.实现哈夫曼编码.哈夫曼译码都定义成 子函数的的形式, 然后在主函数中调用它们...... 数据结构课程设计设计题目: 哈 ...

  8. 二叉树的基本操作及哈夫曼编码/译码系统的实现

    二叉树的基本操作及哈夫曼编码/译码系统的实现 实验目的和要求 掌握二叉树的二叉链表存储表示及遍历操作实现方法. 实现二叉树遍历运算的应用:求二叉树中叶结点个数.结点总数.二叉树的高度,交换二叉树的左右 ...

  9. c语言赫夫曼树的编码与译码,哈夫曼树与编码译码实现

    一.哈弗曼树的基本概念. 哈夫曼树,又称最优树,是一类带权路径长度最短的树.下面有几个概念: (1)路径. 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径. (2)路径长度. 路径上的分枝 ...

  10. 哈夫曼算法证明+哈夫曼编码译码程序实现

    哈夫曼算法证明 哈夫曼算法是一种贪心算法,我们考虑证明其最优子结构和贪心选择性质: 最优子结构:假设一个树是哈夫曼树,则以其任意节点为根节点的最大子树也是哈夫曼树. 证明:子树的根节点的值是其所有叶子 ...

最新文章

  1. react-antd项目中重新npm  install  导致自动升级antd版本,引发的样式问题
  2. TextVew中文空格
  3. python3 和 python2 并存 重新安装pip3
  4. 使用eclipse 进行 Cesium 开发
  5. ARM指令寻址方式之: 内存访问指令寻址
  6. WGAN-GP与GAN及WGAN的比较
  7. 使用Eric构建Caffe应用程序-Baby年龄识别
  8. 果断拿下4000万美元D轮融资,Rancher发力中国本土化与国产化!
  9. JavaScript 的 Date 方法的使用
  10. AndroidStudio中添加第三库文件的方法
  11. ISE14.7逻辑综合与实现工作过程
  12. 二分搜索:lower_bound 与 upper_bound 函数
  13. Java和C语言动态构造int数组
  14. 软件测试——JUnit基础
  15. [转]虚方法(virtual)和抽象方法(abstract)的区别
  16. 《精通开关电源设计》笔记
  17. java网页保存成pdf_JavaScript+Java实现HTML页面转为PDF文件保存的方法
  18. Coverity 配置coverity扫描python静态代码检测
  19. 使用Markdown制作简历
  20. idea2022.1版本创建maven项目没有src文件夹

热门文章

  1. python泰坦尼克号生存预测论文_python泰坦尼克号生存预测
  2. 长微博生成器 php,【九零天下】长微博生成器的PHP代码部分
  3. 浏览器右下角总是弹出广告
  4. 平稳性的检验 java_[时间序列分析]--平稳性,白噪声的检验
  5. Python 爬虫--网站下载器
  6. 报童问题求解最大利润_数据分析案例:用数学建模和数据模拟解决供求矛盾问题...
  7. 2021年PTCMS4.3最新采集规则13条
  8. pdf转word文档保留原格式 本地离线软件
  9. 从零开始学java第一章 认识java
  10. 微信小程序超市购物+后台管理系统|前后分离VUE