- 浏览: 131681 次
- 性别:
- 来自: Ottawa
最新评论
-
God一冰魄:
jcs130 写道thebest 写道感觉保存JSON数据会很 ...
MongoDB 3.0 速上手教程(JAVA) -
xiuxiuxiu:
膨胀腐蚀大概是什么意思,能留个微信吗
结合OPENNI2,Aruco与OPENCV进行视觉定位 -
jcs130:
thebest 写道感觉保存JSON数据会很浪费空间啊。如果每 ...
MongoDB 3.0 速上手教程(JAVA) -
thebest:
感觉保存JSON数据会很浪费空间啊。如果每个记录的标签都是重复 ...
MongoDB 3.0 速上手教程(JAVA) -
jcs130:
liuyar 写道更新很快嘛。加油。谢谢啦,不过接下来要继续研 ...
WiFi遥控小车(三):搭建嵌入式Linux开发环境
搞了一天终于把哈夫曼压缩搞好了,自己想了很多,也参照了别人的代码,终于把自己的做出来了,
关于代码,就不多做说明了,因为思路都是差不多的,代码会在文章最后面贴出来,那我就讲讲几个我遇到的几个问题吧:
1.static尽量不要用:以前我编写什么程序,静态变量太好用了~加个点一引用就好了,不需要传来传去~但是这次我算得到了教训,为了方便,一开始我爸很多变量都设成了静态的,但是邓树构造完成以后,获得叶节点编码的时候,就是一串000000000……找了半天错误不知道错在哪,就把所有的静态变量都设为了private 来回传,结果这样就对了……所以静态变量要少用,尤其是在需要进行递归的场合,更要多加注意!
2.缓冲流可以加快写入速度,但是一定要记着flush:在不用缓冲流的时候,压缩小文件的时候速度还是可以接受的,但是要压缩一个3兆的图片的时候就要等很长时间了,这是把文件输出流变成缓冲流就能节省时间,但是我在一开始用的时候,忘记了在每次写入缓冲流的时候强制刷新(flush),结果导致压缩后的文件异常的小,本来几百K的文件压缩完了只剩下一百多K,一开始我还挺高兴~想这个压缩的效率还挺高~但是当我压缩一个只有4Kb的小文件的时候……压缩出来的文件竟然是0Kb……囧……经过同学的提醒意识到了应该强制刷新一下,这下就正常了。
3.关于压缩的效率:哈夫曼压缩是无损压缩,所以当我压缩一个色彩比较丰富的BMP位图(3M)
压缩完以后,大小没变多少
但是当我压缩一个我自己建的一个重复数据很多的文件的时候,大小的改变就很明显了:
压缩前的大小:
压缩后的大小:
4.最后补0的个数:因为不一定哈弗曼编码的总长度是8的倍数,所以要在后面补0,这样就需要一个用来记录补了多少个0的数据,方便再解压缩的时候读取。
解压缩就是压缩的逆过程(说起来简单啊……╮(╯▽╰)╭),下一步就是要把我压缩的文件还原出来~加油~~~
压缩的代码很长……如下:
package 哈夫曼压缩; /** * 哈夫曼树节点类 * * @author Micro * */ public class HafNode implements Comparable<HafNode> { private HafNode father; private HafNode left; private HafNode right; // 字节 private int num; // 出现的频率 private int pl; // 重载构造器 public HafNode(int num, int pl) { this.num = num; this.pl = pl; } // get set方法 public int getPl() { return pl; } public void setPl(int pl) { this.pl = pl; } public HafNode getFather() { return father; } public void setFather(HafNode father) { this.father = father; } public HafNode getLeft() { return left; } public void setLeft(HafNode left) { this.left = left; } public HafNode getRight() { return right; } public void setRight(HafNode right) { this.right = right; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } //设置按照什么顺序排序 @Override public int compareTo(HafNode o) { // 按照频率的自然顺序排序 int P = o.getPl(); return pl - P; } }
记录编码的类
package 哈夫曼压缩; /** * 存储每一个字节的哈弗曼编码和哈夫曼编码的长度 * * @author Micro * */ public class Code { // 哈夫曼编码 private String node; // 编码长度 private int n; // 重载构造器 public Code(int n, String node) { this.n = n; this.node = node; } public String getNode() { return node; } public void setNode(String node) { this.node = node; } public int getN() { return n; } public void setN(int n) { this.n = n; } }
创建哈夫曼树,获得叶节点编码及读取文件写入文件的方法等:
package 哈夫曼压缩; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.PriorityQueue; /** * 读取文件,统计每个字节出现频率 * * @author Micro * */ public class readFile { private static int[] by = new int[256]; private Code SaveCode[] = new Code[256]; private static HafNode root; /** * 1.统计各字节频率,创建节点,并存入优先队列中 2.把优先队列创建成哈夫曼树 * * @param path * :路径 * @throws IOException */ public void readF(String path) throws IOException { // 创建文件输入流——用来获取频率 FileInputStream ins = new FileInputStream(path); // 创建缓冲流 BufferedInputStream bis = new BufferedInputStream(ins); while (bis.available() > 0) { int t = bis.read(); by[t]++; } // 创建优先队列 PriorityQueue<HafNode> queue = new PriorityQueue<HafNode>(); // 把所有节点都放入队列中 for (int i = 0; i < by.length; i++) { if (by[i] != 0) { HafNode node = new HafNode(i, by[i]); queue.add(node); } } // 构建哈夫曼树 HafTree(queue); } // 创建哈夫曼树 public void HafTree(PriorityQueue<HafNode> queue) { while (queue.size() > 1) { HafNode min1, min2; min1 = queue.poll(); min2 = queue.poll(); // 创建合并的节点 HafNode result = new HafNode(min1.getNum() + min2.getNum(), min1.getPl() + min2.getPl()); result.setLeft(min1); result.setRight(min2); min1.setFather(result); min2.setFather(result); queue.add(result); } root = queue.peek(); } // 获得各叶节点的哈弗曼编码 public void getCode(HafNode a, String Code) { if (a.getLeft() == null && a.getRight() == null) { System.out.println("字节" + a.getNum() + "的哈夫曼编码为:\t" + Code); Code b = new Code(Code.length(), Code); SaveCode[a.getNum()] = b; } if (a.getLeft() != null) { getCode(a.getLeft(), Code + '0'); } if (a.getRight() != null) { getCode(a.getRight(), Code + '1'); } } /** * 将读取的文件的字节转换成哈夫曼编码写入压缩文件 1.把每个字节对应的编码长度写入文件,长度256 * 2.从Code数组中依次读取代表字节的哈弗曼编码,并八位八位的转化成int写入文件 * * @param path1 * :要压缩的文件 * @param path2 * :压缩后的文件路径 * @throws IOException */ public void writeFile(String path1, String path2) throws IOException { // 新建文件输出流 FileOutputStream fos = new FileOutputStream(path2); // 包装为缓冲流 BufferedOutputStream bos = new BufferedOutputStream(fos); // 先把每个字节的编码的长度写入文件(长度为256) for (int i = 0; i < SaveCode.length; i++) { if (SaveCode[i] == null) { bos.write(0); bos.flush(); } else { bos.write(SaveCode[i].getN()); bos.flush(); } } // 写出每一个字节对应的编码 int count = 0;// 记录中专的字符个数 int i = 0;// 第i个字节 String writes = ""; String tranString = "";// 中转的字符串 String waiteString;// 保存所转化的所有字符串 while ((i < 256) || (count >= 8)) { // 如果缓冲区的字节数大于等于8 if (count >= 8) { waiteString = "";// 清空要转化的码 for (int t = 0; t < 8; t++) { waiteString = waiteString + writes.charAt(t); } // 将writes前八位删掉 if (writes.length() > 8) { tranString = ""; for (int t = 8; t < writes.length(); t++) { tranString = tranString + writes.charAt(t); } writes = ""; writes = tranString; } else { writes = ""; } count = count - 8;// 写出一个8位的字节 int intw = changeString(waiteString);// 转化为int // 写入文件 bos.write(intw); bos.flush(); // System.out.println("写入了->"+waiteString); } else { // 得到第i个字节的编码信息,等待写入 if (SaveCode[i] != null) { count = count + SaveCode[i].getN(); writes = writes + SaveCode[i].getNode(); } i++; } } // 把编码没有足够8的整数倍的String补0凑8,再写入 if (count > 0) { // 编码最后补的0的个数 int endz = 0; waiteString = "";// 清空要转化的代码 for (int t = 0; t < 8; t++) { if (t < writes.length()) { waiteString = waiteString + writes.charAt(t); } else { waiteString = waiteString + '0'; endz++; } } bos.write(changeString(waiteString)); bos.flush(); bos.write(endz); bos.flush(); System.out.println("编码区写入了->" + endz + "个0"); } else { System.out.println("编码区写入了->" + 0 + "个0"); bos.write(0); bos.flush(); } /************************ 再次读入文件信息,对应每一个字节写入编码 *********************/ // 再次读入文件信息,对应每一个字节写入编码 // 用来读取数据的文件输入流 FileInputStream ins = new FileInputStream(path1); // 包装成缓冲流 BufferedInputStream bis = new BufferedInputStream(ins); count = 0; writes = ""; tranString = ""; int idata = bis.read(); // 文件没有读完的时候 while ((bis.available() > 0) || (count >= 8)) { // 如果缓冲区等待字符大于等于8 // System.out.println(count+"<<>>"+writes.length()); if (count >= 8) { waiteString = "";// 清空要转化的码 for (int t = 0; t < 8; t++) { waiteString = waiteString + writes.charAt(t); } // 删除前八位 writes = writes.substring(8); count -= 8;// 写出一个8位的字节 int intw = changeString(waiteString); bos.write(intw);// 写入到文件中 bos.flush(); } else { // 读入idata字节 ,对应编码写出信息 count += SaveCode[idata].getN(); writes += SaveCode[idata].getNode(); // System.out.println(SaveCode[idata].getN()+"<<>>"+SaveCode[idata].getNode().length()); idata = bis.read(); } } count += SaveCode[idata].getN(); writes += SaveCode[idata].getNode(); // 把count 剩下的写入 int endsint = 0; if (count > 0) { waiteString = "";// 清空要转化的码 for (int t = 0; t < 8; t++) { if (t < writes.length()) { waiteString += writes.charAt(t); } else { waiteString += '0'; endsint++; } } // System.out.println(waiteString.length()); bos.write(changeString(waiteString));// 写入最后补的0 bos.flush(); bos.write(endsint);// 写入最后补的0的个数 bos.flush(); System.out.println("压缩区写入了->" + endsint + "个0"); } } /** * 将八位字符串转化为一个整数 * * @param s * @return */ public int changeString(String s) { return ((int) s.charAt(0) - 48) * 128 + ((int) s.charAt(1) - 48) * 64 + ((int) s.charAt(2) - 48) * 32 + ((int) s.charAt(3) - 48) * 16 + ((int) s.charAt(4) - 48) * 8 + ((int) s.charAt(5) - 48) * 4 + ((int) s.charAt(6) - 48) * 2 + ((int) s.charAt(7) - 48); } // 程序入口 public static void main(String[] args) throws IOException { readFile rf = new readFile(); // 要压缩的文件路径 String path = "D:\\新建文本文档.txt"; // 压缩后的文件路径 String path1 = "D:\\123.txt"; rf.readF(path); // 获得各叶节点的哈弗曼编码 String Code = ""; rf.getCode(root, Code); // 写入压缩文件 rf.writeFile(path, path1); } }
发表评论
-
MongoDB 3.0 速上手教程(JAVA)
2015-08-18 08:34 18544最近做项目想用一下NoSQL数据库,由于项目需要保存大量的 ... -
基于百度地图API的简单地图程序
2012-11-03 16:54 14028前一阵子我们物联网编程课老师要求班上每个人都要 ... -
WiFi遥控小车(四):简单直流电机驱动及UDP通信程序
2012-10-22 20:12 3117之前说过只要能控制高低电平就可以控制电机的正反转,我想通过修 ... -
WiFi遥控小车(三):搭建嵌入式Linux开发环境
2012-10-22 00:21 5212我是在暑假前买的开发板,本来想这暑假就开始学,但是 ... -
WiFi遥控小车(三):搭建嵌入式Linux开发环境
2012-10-22 00:08 3我是在暑假前买的开发板,本来想这暑假就开始学,但是跟着 ... -
WiFi遥控小车(二):选择学习&开发平台
2012-10-21 01:50 5010前面说到我想自己 ... -
WiFi遥控小车(一):基于wicam模块的小车
2012-10-20 13:20 9045小的时候很喜欢玩四驱车,看动画片《四驱兄弟2》 ... -
推荐几个前端开发很容易上手的软件~~
2012-08-07 23:13 1250今天终于做了WEB阶段的项目总结~我们的微博项目差不多的基本 ... -
想把网页做漂亮了真不容易~美工们~你们辛苦了——菜鸟学WEB有感
2012-07-22 03:28 2822这几天学Web,我们小组 ... -
我就是喜欢界面~~可视化打印哈弗曼树(二叉树)~JAVA实现
2012-07-09 05:03 6283课程设计有一个哈夫曼编码解码的题,其他的一般要求还好说~就是最 ... -
探究C语言中的getchar()与getch()的区别
2012-03-10 14:58 1736最近大家 ... -
菜鸟说:哈夫曼压缩的解压缩~~(附源代码)
2011-08-13 10:37 1233解压就是压缩的逆过程……真是说起来容易做起来难啊…… ... -
菜鸟说:哈夫曼树及哈弗曼编码
2011-08-11 14:52 1178终于把哈夫曼树及哈弗曼编码弄好了~ 哈夫曼树就是最优二 ... -
初识“树”——搜索二叉树
2011-08-10 23:51 1076今天第一次接触了“树 ... -
菜鸟说java里的链表(一)
2011-08-10 00:52 1539先说点题外话: 不知道为什么,我们学校还有别的好多学校的软件 ...
相关推荐
这个项目“哈夫曼压缩——GUI”是基于Visual Studio 2010开发的一个图形化界面工具,对于学习数据结构和算法的学生来说,是一个很好的实践案例。 1. **哈夫曼编码**:哈夫曼编码是一种变长的前缀编码方式,通过构建...
内容概要:本文介绍了哈夫曼树的基本概念及其在数据压缩中的应用。首先解释了何为哈夫曼树,即带权路径长度最短的二叉树,适用于前缀编码。接着详细描述了构建哈夫曼树的步骤:初始化节点、选择并合并最小频率节点...
总结来说,哈夫曼树压缩算法利用了数据的统计特性,通过构建最优二叉树实现高效的数据编码,进而完成数据的压缩。在实际应用中,这种算法广泛应用于文本、图像和音频等数据的压缩,提高了存储和传输的效率。
* compress:压缩算法的实现 * decompress:解压缩算法的实现 * create_filename:生成输出文件名 * write_compress_file:将哈夫曼编码写入输出文件 * write_decompress_file:将解压缩后的数据写入输出文件 6. ...
内容概要:本文详细介绍了哈夫曼编码在数据库压缩中的应用,包括哈夫曼树的构建算法、编码生成、数据压缩与解压缩的具体实现。通过构建哈夫曼树,根据字符频率生成唯一的二进制编码,从而有效减少存储空间和提高数据...
Java哈夫曼编码是一种数据压缩技术,它基于哈夫曼树进行编码,通过构建最优的二叉树结构来实现高效的数据编码与解码。在Java中实现哈夫曼压缩和解压涉及到以下几个关键知识点: 1. **哈夫曼树**: 哈夫曼树...
总的来说,哈夫曼压缩算法是一种重要的数据压缩技术,它基于字符频率统计和最优二叉树构建,通过为字符分配不同的二进制编码实现数据的高效压缩。在C#编程环境下,我们可以利用面向对象的特性,设计和实现哈夫曼压缩...
实验七:哈夫曼树.docx
哈夫曼编码是一种变长前缀编码技术,广泛应用于数据压缩和通信领域。哈夫曼树是哈夫曼编码的核心数据结构,它是一种特殊的二叉树结构。 哈夫曼树的定义: 哈夫曼树是一种特殊的二叉树结构,它的每个叶子结点对应一...
哈夫曼编码(Huffman Coding)是一种数据压缩算法,由美国计算机科学家大卫·艾尔曼在1952年提出。它的主要原理是基于字符出现频率构建最优的二叉树,进而为每个字符生成最短的唯一编码,使得常用字符在编码过程中...
10. **注意事项**:在实现哈夫曼压缩程序时,需要注意编码的前缀冲突问题,以及在解压缩时如何正确地识别每个字符的边界,这通常需要用到一些辅助信息,比如编码表和原始数据的长度。 综上所述,"C++哈夫曼压缩...
哈夫曼压缩是一种高效的数据编码方法,主要用于无损数据压缩,其原理是基于字符出现频率构建...在实际编程中,可能会遇到如何优化树的构建、如何高效地存储和读取编码等问题,这些都是进一步提升哈夫曼压缩性能的关键。
vc++哈夫曼压缩算法 vc++哈夫曼压缩算法
哈夫曼编码是一种高效的数据压缩方法,由大卫·哈夫曼在1952年提出。它是基于频率的变字长编码技术,适用于无损数据压缩。在Java中实现哈夫曼压缩通常包括以下几个关键步骤: 1. **构建哈夫曼树**:首先,需要统计...
哈夫曼编码是一种高效的数据压缩算法,由大卫·哈夫曼在1952年提出。它是基于贪心策略的,主要用于无损数据压缩。在本文中,我们将深入探讨哈夫曼编码的基本原理、实现过程以及如何在VC++环境下利用MFC(Microsoft ...
哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩...
### 哈夫曼压缩详解 #### 一、哈夫曼压缩概述 哈夫曼压缩是一种广泛应用于数据压缩领域的无损压缩技术。无损压缩意味着压缩后的数据在解压后可以完全恢复到原始状态,不丢失任何信息。哈夫曼编码通过构建一个特殊...
哈夫曼编码(Huffman Coding)是一种数据压缩算法,由美国计算机科学家大卫·哈夫曼在1952年提出。这种编码方法基于频率最小的字符优先的原则,通过构建一棵特殊的二叉树(哈夫曼树)来实现对数据的高效压缩。在C++...