哈夫曼压缩
2015-08-31 11:31
253 查看
此文主要分析的是哈夫曼压缩的重点包括统计字符频率,建哈夫曼树,生成码表。哈夫曼压缩是最常用的一种静态无痕压缩。
以前也学习过哈夫曼的算法结构,但是没有自己去写代码实现,这次再学习了一遍,更加深刻理解哈夫曼压缩的原理,如何真正实现文件的压缩节省内存资源。下面梳理下我的代码和分析逻辑。
第一步是打开文件,读取文件流,统计文字频率。
方法是读取文件内容,根据每个字符有唯一的字节,存储在长度为256的数组中。可将字符和频率绑定为一个节点类Node,所有节点类的对象存储在队列中。
第二步是排序。
根据字符所对应的频率由小到大进行排序,可用选择排序算法。
第三步是构建哈夫曼二叉树。
依次取出排好序的节点,每次取出前两个生成一个父节点,再将父节点添加到队列中,这里我将生成父节点的过程写在另一个函数creatHFM里,方便理解逻辑。
第四步是给每个字符编码,生成码表。
用递归遍历的方法生成每一个字符的编码,左0右1,并绑定字符保存,即新建一个类,依次保存到队列中。
在以上代码中所有的方法的参数输入由上一个方法的返回决定,更加方便梳理逻辑,查错。
构造方法及主函数,其中主函数中测试文件中的内容为string
测试结果
大小: 2.5 KB
查看图片附件
以前也学习过哈夫曼的算法结构,但是没有自己去写代码实现,这次再学习了一遍,更加深刻理解哈夫曼压缩的原理,如何真正实现文件的压缩节省内存资源。下面梳理下我的代码和分析逻辑。
第一步是打开文件,读取文件流,统计文字频率。
方法是读取文件内容,根据每个字符有唯一的字节,存储在长度为256的数组中。可将字符和频率绑定为一个节点类Node,所有节点类的对象存储在队列中。
/* * 统计频率 */ private static Mylist<Node> countFre(String path) throws IOException { // 创建一个文件输入流对象 java.io.FileInputStream fis = new java.io.FileInputStream(path); java.io.BufferedInputStream bis = new java.io.BufferedInputStream(fis); int[] byteArray = new int[256]; while (bis.available() > 0) { int i = bis.read(); // 统计字符的频率 byteArray[i]++; } // 关闭文件输入流 fis.close(); Mylist<Node> l = new Mylist<Node>(); // 遍历数组,打印并保存 for (int i = 0; i < byteArray.length; i++) { if (byteArray[i] != 0) { char c = (char) i; int count = byteArray[i]; // System.out.println("字符:"+c+" "+"频率:"+count); // 添加到队列 l.add(new Node(c, count)); } } return l; }
第二步是排序。
根据字符所对应的频率由小到大进行排序,可用选择排序算法。
/* * 排序 */ private static void sort(Mylist<Node> list) { for (int i = 0; i < list.size() - 1; i++) { int min = i; for (int j = i + 1; j < list.size(); j++) { // 如果j返回的节点中的频率大于i if (list.get(j).getFrequency() < list.get(min).getFrequency()) min = j; } // 交换 if (i != min) { Node n = list.get(min); list.set(list.get(i), min); list.set(n, i); } } }
第三步是构建哈夫曼二叉树。
依次取出排好序的节点,每次取出前两个生成一个父节点,再将父节点添加到队列中,这里我将生成父节点的过程写在另一个函数creatHFM里,方便理解逻辑。
/* * 构建哈夫曼二叉树 */ private static Node creat(Mylist<Node> list){ while (list.size() > 1) { Node l = list.remove(0); Node r = list.remove(0); Node n = creatHFM(l, r); // 把新的节点添加到队列中 list.add(n); // 排序 sort(list); } //根节点 Node root = list.get(0); return root; } private static Node creatHFM(Node left, Node right) { int fc = left.getFrequency() + right.getFrequency(); Node root = new Node(' ', fc); root.setLeft(left); root.setRight(right); return root; }
第四步是给每个字符编码,生成码表。
用递归遍历的方法生成每一个字符的编码,左0右1,并绑定字符保存,即新建一个类,依次保存到队列中。
/* * 计算编码 */ private static void creatCode(Node root, String str) { // 若不是叶节点,递归遍历 if (root.getLeft() != null) { creatCode(root.getLeft(), str + "0"); creatCode(root.getRight(), str + "1"); } else {// 否则输出,编码,并保存 count += root.getFrequency() * str.length(); System.out.println(root.getC() + " = " + str); // 保存每一个字符及其编码,存入队列 Character chact = new Character(root.getC(), str); codelist.add(chact); } }
在以上代码中所有的方法的参数输入由上一个方法的返回决定,更加方便梳理逻辑,查错。
构造方法及主函数,其中主函数中测试文件中的内容为string
public HFMtree(String path) throws IOException { // 统计每个字符的频率 list = countFre(path); // 根据每个字符的频率排序 sort(list); // 构建哈夫曼二叉树,返回根节点 Node root = creat(list); // 生成每一个字符的编码及带权路径。并保存编码 creatCode(root, ""); } /* * 主函数 */ public static void main(String[] args) throws IOException { // 文件路径 String str = "F:/text.txt"; // 文件的内容 // String string = "jfsjflvyavllcyqlcjhqvYV"; HFMtree hfm=new HFMtree(str); System.out.print("带权路径=" + count); }
测试结果
大小: 2.5 KB
查看图片附件
相关文章推荐
- Android界面布局
- win2003中iis与asp设置Session会话过期时间的问题
- 扫雷小游戏的设计与实现
- 类和对象的理解
- 接口与抽象类
- 画板感想与小结
- 批处理命令调用WINRAR对文件进行压缩
- 我的生活,我的积累
- 几种常见数据库连接池的使用比较
- UIViewController方法调用顺序
- 一次向svn中增加所有新增文件 svn add all new files
- 关于友元,重载的使用说明
- 硬件十万个为什么——运放篇(十)输入阻抗
- dom解析xml之中文乱码问题
- InvocationHandler中invoke()方法的调用问题
- The ROI Variable
- 【mysql】iBator 数据库操作(自动生成文件)
- SQLServer优化资料整理(一)
- WEB项目中配置属性加密的方式
- 安卓的优缺点