[size=large]
哈弗曼简单实现压缩
了解哈弗曼树就先从简单的二叉树讲起:
二叉树:有一个根结点及其叶子结点组成、一个结点下只能接两个叶子结点:分别叫做左子树和右子树;其存储信息和链表的结点大致相似。
如图所示:[/size]
[img]http://dl.iteye.com/upload/attachment/0071/6184/a2dbfddf-3638-3dd1-a9b5-a296cb22dbb3.png[/img]
[size=large]
[size=large]
二叉树的遍历可分为四种:先序遍历、中序遍历、后序遍历以及层次遍历。
先序遍历:先访问根结点再访问左子树、右子树。
中序遍历:先访问左子树、再访问根结点、右子树。
后序遍历:先访问左子树、右子树、根结点。
层次遍历:一层一层从上往下、从左往右遍历。
区分先、中后序遍历的最好方法是根据左子树遍历的先后;而且左子树始终在右子树前面。
了解二叉树后、我们需要知道权值的概念:在数据结构中,权值就是定义的路径上面的值。
例如长沙,广州分别是两个端点,两个端点可以用边相连表示可达,长沙到广州800km,那么就说边的权值是800.
这样、二叉树通过赋权值使得其形成的权值不同;而我们做哈弗曼压缩时、需要的是权值最小
的树即最优树。
例:[/size]
[img]http://dl.iteye.com/upload/attachment/0071/6191/e7299b8e-3709-3d40-88a0-e8fe35804c90.png[/img]
[size=large]
这样、压缩所需要的哈弗曼树有了,接着我们要遍历压缩的内容并用数组统计其出现的频率、
统计完后、通过数组中统计的频率构造哈弗曼树;在构造过程中、生成好的二叉树将叶子节点
以左0右1的方式分配编码;再用数组存装对应字符及其相应哈弗曼编码、压缩时,读入头文
件、将数组中哈弗曼编码长度写入压缩文本、接着将每个字符的对应的哈弗曼编码连接;当大
于8个字节时化成整形写入文件;最后不足8个时需要补0.这就是压缩头文件。。。
例:[/size]
[size=large]
压缩文件内容:一边遍历文件内容、一边将读到的字节一个一个地转成哈弗曼编码、再连接;
当大于8个01串长度时就进行转化;并将转化后的int型写入。写入之后要记得删除以转化的
01串;这样转化直至文件末尾;如果文件末尾01串长度不足8位、则需要补0;并将补0的个
数写入文件;[/size]
[size=large]
解压的过程是一个逆反过程:也是先读出头文件、再读出文件内容并读出的内容转成01串与读出的头文件匹配、、如果匹配成功则写入文件、值得注意的是倒数第二个有补0的情况、这就需要将最后一位补零个数的字节读出来;减去。这样再看是否能继续转化。。。
[/size]
[/size]
[size=x-large]
如果浏览还不清楚、可以下载源代码;谢谢指教 :lol:
[/size]
哈弗曼简单实现压缩
了解哈弗曼树就先从简单的二叉树讲起:
二叉树:有一个根结点及其叶子结点组成、一个结点下只能接两个叶子结点:分别叫做左子树和右子树;其存储信息和链表的结点大致相似。
如图所示:[/size]
[img]http://dl.iteye.com/upload/attachment/0071/6184/a2dbfddf-3638-3dd1-a9b5-a296cb22dbb3.png[/img]
[size=large]
[size=large]
二叉树的遍历可分为四种:先序遍历、中序遍历、后序遍历以及层次遍历。
先序遍历:先访问根结点再访问左子树、右子树。
中序遍历:先访问左子树、再访问根结点、右子树。
后序遍历:先访问左子树、右子树、根结点。
层次遍历:一层一层从上往下、从左往右遍历。
区分先、中后序遍历的最好方法是根据左子树遍历的先后;而且左子树始终在右子树前面。
了解二叉树后、我们需要知道权值的概念:在数据结构中,权值就是定义的路径上面的值。
例如长沙,广州分别是两个端点,两个端点可以用边相连表示可达,长沙到广州800km,那么就说边的权值是800.
这样、二叉树通过赋权值使得其形成的权值不同;而我们做哈弗曼压缩时、需要的是权值最小
的树即最优树。
例:[/size]
[img]http://dl.iteye.com/upload/attachment/0071/6191/e7299b8e-3709-3d40-88a0-e8fe35804c90.png[/img]
[size=large]
这样、压缩所需要的哈弗曼树有了,接着我们要遍历压缩的内容并用数组统计其出现的频率、
统计完后、通过数组中统计的频率构造哈弗曼树;在构造过程中、生成好的二叉树将叶子节点
以左0右1的方式分配编码;再用数组存装对应字符及其相应哈弗曼编码、压缩时,读入头文
件、将数组中哈弗曼编码长度写入压缩文本、接着将每个字符的对应的哈弗曼编码连接;当大
于8个字节时化成整形写入文件;最后不足8个时需要补0.这就是压缩头文件。。。
例:[/size]
// 创建文件输入流对象
FileInputStream fis = new FileInputStream(path);
// //创建文本缓冲流对象
// BufferedInputStream bis=new BufferedInputStream(fis);
// //实例化一个缓冲输出流对象
// BufferedOutputStream bos=new BufferedOutputStream(fos);
// 实例化一个输出流对象
FileOutputStream fos = new FileOutputStream(path + "压缩文件.txt");
// 写出每一个字节对应编码的长度
for (int j = 0; j < saveCode.length; j++) {
// System.out.println("请输出对应节点的长度" + saveCode[j]);
// if (saveCode[j].getLength() != 0) {
// 先 写入asc码所对应的下表
// fos.write(j);
// 写入asc码中每个编码的01串长度
fos.write(saveCode[j].getLength());
System.out.println("字符"+((char)j)+"的编码:" + "------->"
+ saveCode[j].getNode());
// 将01串的长度进行累加
reminder += saveCode[j].getLength();
// }
}
// // 计算出补零的个数,并读入
// int remind = reminder % 8;
// // 写入文件,并将remind进行强转
// fos.write(remind);
// System.out.println("jjjjjjjjjjjjjjj" + remind);
/*********************** 写入头文件 **************************************/
// 先声明asc编码的下表
int i = 0;
// 声明缓冲区得到的01串编码
String writes = "";
// 声明要转的01串字符
String waiteString = "";
// 声明一个01串的中转站
String tranString = "";
// 声明01串的个数
int count = 0;
// 进行循环,将sac编码下所有01串字符转化成int型写入文件
while ((i < 256) || (count >= 8)) {
// 如果缓冲区的01串大于8// 如果大于等于8,则写入文件
if (count >= 8) {
// 先将waiteString清零
waiteString = "";
// 先取出前8个01串字符
for (int j = 0; j < 8; j++) {
waiteString += writes.charAt(j);
}
// 将取出的01串转化为int型,且写入文件
// System.out.println("><><><><><><" + waiteString);
//
// System.out.println("&&&&&&&&&&&&&"+changeString(waiteString));
// 判断writes的01串是等于还是大于8
if (writes.length() > 8) {
//清空中转字符
tranString="";
// 将writes的01串减8
for (int t = 8; t < writes.length(); t++) {
// 将剩下的01串移到tranString上
tranString += writes.charAt(t);
}
//再将writes清空
writes="";
// 再将writes指针指回剩下的01串
writes = tranString;
}
// 如果是等于8
else {
writes = "";
}
// 再将count数量减8
count -= 8;
//定义一个变量来接收转化成int型的数
int intw=changeString(waiteString);
fos.write(intw);
// 如果01串不足8个
}else {
// System.out.println("--------->");
// 判断对应asc编码是否存在
// if(saveCode[i]!=null){
// 得到01串的个数
count += saveCode[i].getLength();
// 得到01串编码
writes += saveCode[i].getNode();
// System.out.println(saveCode[i].getLength() + "<><><><><><><><>"
// + saveCode[i].getNode());
// }
System.out.println("i:"+i+">>>>>>>>>>"+saveCode[i].getNode());
i++;
}
}
System.out.println("跳出循环");
// 最后循环累加之后、剩下不足8个01串,则进行补0
if (count > 0) {
// 定义一个计数器;记录补零的个数
// int k = 0;
waiteString = "";// 清空要转化的的码
for (int t = 0; t < 8; t++) {
if (t < writes.length()) {
waiteString = waiteString + writes.charAt(t);
// System.out.println("**********************" + t);
} else {
// k++;
waiteString = waiteString + '0';
}
}
fos.write(changeString(waiteString));// 写入
// 写入头文件补0 的个数
// fos.write(k);
// System.out.println("llllllllllll" + k);
System.out.println("写入了->" + waiteString);
}
[size=large]
压缩文件内容:一边遍历文件内容、一边将读到的字节一个一个地转成哈弗曼编码、再连接;
当大于8个01串长度时就进行转化;并将转化后的int型写入。写入之后要记得删除以转化的
01串;这样转化直至文件末尾;如果文件末尾01串长度不足8位、则需要补0;并将补0的个
数写入文件;[/size]
/*************** 写入文件内容 ************************************************/
// 得到读入字符的ascii编码的值
int idata = fis.read();
// System.out.println("文件内容是:" + idata);
// 缓冲区得到的01串编码
writes = "";
// 要转的01串字符
tranString = "";
// 01串的个数
count = 0;
// 文件没有读完的时候
while ((fis.available() > 0) || (count >= 8)) {
// 如果缓冲区的01串码大于等于8,则进行转化
if (count >= 8) {
// 先将waiteString清零
waiteString = "";
// 截取前8个01串并转化
for (int t = 0; t < 8; t++) {
waiteString += writes.charAt(t);
}
// 将取得的01串转化并写入
// System.out.println("转化的字符是:" + waiteString);
// 判断01串是大于8还是等于8
if (writes.length() > 8) {
// 01串的中转站
tranString = "";
// 再将01串减8
for (int t = 8; t < writes.length(); t++) {
tranString += writes.charAt(t);
}
writes="";
// 将writes指针指回
writes = tranString;
System.out.println("打印出剩下的01串:" + writes);
} else {
// 如果等于8
writes = "";
}
// 将前八个01串删除
count -= 8;
int intw=changeString(waiteString);
fos.write(intw);
} else {
// 读入idata字节,对应编码写出信息
count = count + saveCode[idata].getLength();
// 将字符所对应的01串编码进行累加
writes += saveCode[idata].getNode();
// System.out.println("<---------------->" + count + "===="
// + writes);
// 将idata往下移
idata = fis.read();
// System.out.println("//////" + saveCode[idata].getLength()
// + "//////" + saveCode[idata].getNode());
}
}
// 再将读取的字符的01串长度进行累加
count += saveCode[idata].getLength();
// 将01串进行累加
writes = writes + saveCode[idata].getNode();
// 记录补零的个数
int endsint = 0;
if (count > 0) {
// 清空待转化的字符
waiteString = "";
// 将剩下的01串转到waiteString上
for (int t = 0; t < 8; t++) {
if (t < writes.length()) {
waiteString = waiteString + writes.charAt(t);
} else {// 剩下的补零
waiteString = waiteString + '0';
endsint++;
}
}
// 补完0 后,进行转化写入
fos.write(changeString(waiteString));
System.out.println("最后的字符已经写入了!!!!");
}
// System.out.println("ttttttttttt" + endsint);
// 将补零的个数写入
fos.write(endsint);
System.out.println("压缩完毕。。。。");
fis.close();
fos.close();
[size=large]
解压的过程是一个逆反过程:也是先读出头文件、再读出文件内容并读出的内容转成01串与读出的头文件匹配、、如果匹配成功则写入文件、值得注意的是倒数第二个有补0的情况、这就需要将最后一位补零个数的字节读出来;减去。这样再看是否能继续转化。。。
[/size]
java.io.FileInputStream fis = new java.io.FileInputStream(path2);
// 实力化输入流对象
java.io.FileOutputStream fos = new java.io.FileOutputStream(path2 + "解压文件.txt");
// 读入文件,得到asc码数组的值
for (int i = 0; i < 256; i++) {
// 先读出asc码下表
Code code = new Code();
// 读出每个ascii码的01串长度
code.setLength(fis.read());
code.setNode("");
// 加入到数组中
codeArray[i] = code;
// System.out.println("--------->"+i+codeArray[i].getLength());
}
// int reminder = fis.read();
// System.out.println("编码长度读取完毕" + reminder);
/********************** 读入头文件 **********************/
// 解压头文件,得到 sacii码的01串//读SaveCode中0~256字节的的数据
// 声明一个从文件中读出的com变量
String coms = "";
// 声明一个从0开始计数的变量
int i = 0;
while (i < 256) {
// 当缓存区的编码比codeArray[i]的长度长时
if (coms.length() >= codeArray[i].getLength()) {
// System.out.println("================="+i+":"+coms+"<>"+codeArray[i].getLength());
// 取得该编码
for (int t = 0; t < codeArray[i].getLength(); t++) {
// 将coms上的字符转到codeArray[i]的node中
codeArray[i].setNode(codeArray[i].getNode()
+ coms.charAt(t));
}
// System.out.println("现在"+((char)i)+"的编码是:" + codeArray[i].getNode());
// 去掉coms前几位
String coms2 = "";
for (int j = codeArray[i].getLength(); j < coms.length(); j++) {
coms2 += coms.charAt(j);
}
coms="";
// 将coms指针回指
coms = coms2;
System.out.println("<><><><><><><>"+i+codeArray[i].getNode());
// 往后移
i++;
}
// 如果没有写入的话,再读入一个byte化为01串
else {
// System.out.println("-------->"+coms);
coms = coms + intToString(fis.read());
// System.out.println("<><><><><><><><><><><"+coms);
}
[/size]
[size=x-large]
如果浏览还不清楚、可以下载源代码;谢谢指教 :lol:
[/size]
本文详细介绍了哈弗曼树的基本概念、构建原理及其实现步骤,包括如何利用二叉树进行数据压缩,以及压缩和解压的具体流程。
1万+

被折叠的 条评论
为什么被折叠?



