- 浏览: 22449 次
- 性别:
- 来自: 杭州
最新评论
人一旦闲下来就很恐怖的,已经好久都没写博客了,热情也不如先前的。忙碌的时候,每做完一件事,我就一种自豪的感觉。我不想让自己活得很空虚。所以还得写点东西。
对于哈夫曼树,我们的解释是每次取出一组数组里的最小两个数来建树,把这两者的和放到
原先的数组。作为新的元素。重复以上操作,来建树。
以下的步骤只是我的理解
1.统计文件中字节出现的次数,并把次数作为结点来键哈夫曼树。
如何来建树,这里出现了问题。我们实际是通过优先队列来进行建树的。这个队列里的数,
按从小到大进行的排序,仿佛这个队列就是为哈夫曼树而生。
统计次数的方法。
public int[] FileRead(String path){
//创建文件输入流
try {
FileInputStream fils = new FileInputStream(path);
//将文件输入流包装成缓冲流
BufferedInputStream fos=new BufferedInputStream(fils);
//读入字节
int a=fos.read();
while(a!=-1){
array[a]++;
//读取下一个字节
a=fos.read();
}
} catch (Exception ef) {
ef.printStackTrace();
}
return array;
}
建树的过程
/**
* 通过优先队列来构造哈夫曼树,返回根结点
*/
public TreeNode2 CreatTree(PriorityQueue<TreeNode2> list){
while(list.size()>1){
//取出两个构造
TreeNode2 node1=list.poll();
TreeNode2 node2=list.poll();
//创建父结点
TreeNode2 nodeparent=new TreeNode2();
nodeparent.sum=node1.sum+node2.sum;
list.add(nodeparent);
//建立关系
nodeparent.lchild=node1;
node1.parent=nodeparent;
nodeparent.rchild=node2;
node2.parent=nodeparent;
//给结点附哈夫曼编码
node1.flag1=0;
node2.flag1=1;
}
TreeNode2 lastnode=list.poll();
return lastnode;
}
这里传入的参数优先队列,因为系统的优先队列不能排序根结点,所以需要自己重写其中
的比较方法。
/**
* 将读入的数据存入优先队列
*/
public PriorityQueue<TreeNode2> arraylist(int array[]){
//根据指定的比较器创建一个优先队列
PriorityQueue<TreeNode2> list = new PriorityQueue<TreeNode2>(11,new MyComparator());
//将一个个结点对象存入到优先队列中
for(int i=0;i<array.length;i++){
if(array[i]!=0){
TreeNode2 treenode=new TreeNode2();
treenode.obj=i;
treenode.sum=array[i];
list.add(treenode);
}
}
return list;
}
/**
* 写一个类来是实现迭代器这个接口
* @author Administrator
*重写里面的方法compare
*/
class MyComparator implements Comparator<TreeNode2>{
public int compare(TreeNode2 node1,TreeNode2 node2){
int o1=node1.sum;
int o2=node2.sum;
return o1-o2;
}
}
2.通过哈夫曼树进行编码
/**
*
* @param str对应一个节点的哈夫曼编码
* @param root传入的根结点
* @param code哈夫曼编码字符串数组
*/
public void GetoneCode(String str,TreeNode2 root,String code[]){
//首先判断节点是否为空
if(root!=null){
if(root.flag1!=null){
str=str+root.flag1;
if(root.lchild==null&&root.rchild==null){
code[root.obj]=str;
System.out.println(root.obj+"该字节"+"哈夫曼编码为"+str);
//将找到的数据依次输入队列中
Data data=new Data();
data.str=str;
data.obj=root.obj;
list.add(data);
}
}
//递归遍历
TreeNode2 left=root.lchild;
GetoneCode(str,left,code);
TreeNode2 right=root.rchild;
GetoneCode(str,right,code);
}
}
3.利用哈夫曼编码对应的字节来进行读文件,然后把01字符串每8个字符串写成一个字节。
/**
* 文件压缩 并输出
* 将得到的字符串数组,取出字符串每8位变成一个字节
*/
public void OutPut(String path,String oldpath){
//记录文件中的01字符串的个数
int num=0;
//记录文件补0的情况
int addzero=0;
try{
//创建输入流对象
FileInputStream fis=new FileInputStream(oldpath);
//创建输入缓冲流
BufferedInputStream dis=new BufferedInputStream(fis);
//创建输出流对象
FileOutputStream fos=new FileOutputStream(path);
//创建输出缓冲流
BufferedOutputStream dos=new BufferedOutputStream(fos);
/**
* 写头文件,文件内容最后补零的情况
*/
dos.write(list.size());
System.out.println("队列的长度"+list.size());
for(int i=0;i<list.size();i++){
Data data=list.get(i);
dos.write(data.obj);
byte by[]=data.str.getBytes();
dos.write(by.length);
dos.write(by);
num+=array[data.obj]*data.str.length();
}
System.out.println("该数目为"+num);
addzero=8-num%8;//求出添零的个数
dos.write(addzero);
String writeString="";
byte[] by=new byte[(num+addzero)/8];//将变成的字节存在创建的byte数组
System.out.println("数组的长度为"+by.length);
int count=0;
while(dis.available()>0){
int i=dis.read();
//寻找与之匹配的编码
for(int j=0;j<list.size();j++){
boolean bool=false;//标记是否找到
if(list.get(j).obj==i){
int x=0;
while(true){
writeString+=list.get(j).str.charAt(x)+"";
x++;
//如果字符串长度已经达到8位
if(writeString.length()==8){
byte wri=this.change(writeString);
by[count]=wri;//将转化的字节保存到数组中
count++;
writeString="";//将字符串重新初始化
}
//如果字符串长度不足8位但是已经编码完全加入字符串中
if(x==list.get(j).str.length()){
bool=true;
break;//跳出循环
}
}
}
//如果找到,则跳出循环
if(bool==true){
break;
}
}
}
System.out.println("此时的count为"+count);
//如果还有剩余的字符在字符串中,则需要补0
if(writeString.length()>0){
int n=8-writeString.length();
for(int i=0;i<n;i++){
writeString+="0";
}
byte wri=this.change(writeString);//将8位转换为一个byte
by[count]=wri;//写入byte数组
}
//此处的写入的范围为多少
// dos.write(by.length);//写入byte数组的大小
dos.write(by);//写入byte数组
System.out.println("byte的数组长度为"+by.length);
dis.close();//关闭输入流
dos.close();//强制输出
fos.close();//关闭输出流
}catch(Exception e){
e.printStackTrace();
}
}
4.文件的解压
/**
* 将压缩的文件进行解压
*
*/
public void read(String path,String Newpath){
//创建存放数据的队列
ArrayList<Data> list1=new ArrayList<Data>();
try{
//创建输入流
FileInputStream fis=new FileInputStream(path);
//创建输入缓冲流
BufferedInputStream dis=new BufferedInputStream(fis);
//创建输出流
FileOutputStream fos=new FileOutputStream(Newpath);
//创建输出缓冲流
BufferedOutputStream dos=new BufferedOutputStream(fos);
/**
* 读头文件
*/
int size=dis.read();
System.out.println("队列的长度为"+size);
for(int i=0;i<size;i++){
Data data=new Data();
data.obj=dis.read();
int len=dis.read();
byte[] by=new byte[len];
dis.read(by);
data.str=new String(by);
list1.add(data);
}
int addzero=dis.read();//读出补0的情况
// int count=dis.read();//读出byte数组大小
int count=dis.available();
byte[] b=new byte[count];
System.out.println("补零数为"+addzero+"byte数组的大小为"+count);
dis.read(b);//读出byte数组
String str=new String("");
for(int i=0;i<b.length;i++){
int n=(int)b[i];
if(n<0){
n+=256;
}
str=str+this.changeInt(n);
}
//减去01字符串中末尾多加上的0
str=str.substring(0, str.length()-addzero);
boolean bool=true;
while(bool){
bool=false;
//寻找与之对应的字符
for(int i=0;i<list1.size();i++){
if(str.startsWith(list1.get(i).str)){
dos.write((int)list1.get(i).obj);
str=str.substring(list1.get(i).str.length());
bool=true;
break;
}
}
}
//关闭输入输出流
fis.close();
dos.flush();
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
看上去代码的确是很长,其中有些语句,没有深刻理解的话,确实难以弄懂。当然这个压缩也存在居多问题,比如压缩的文件大小太大,否则会卡机。可能是这个程序特别耗cpu导致的。
对于哈夫曼树,我们的解释是每次取出一组数组里的最小两个数来建树,把这两者的和放到
原先的数组。作为新的元素。重复以上操作,来建树。
以下的步骤只是我的理解
1.统计文件中字节出现的次数,并把次数作为结点来键哈夫曼树。
如何来建树,这里出现了问题。我们实际是通过优先队列来进行建树的。这个队列里的数,
按从小到大进行的排序,仿佛这个队列就是为哈夫曼树而生。
统计次数的方法。
public int[] FileRead(String path){
//创建文件输入流
try {
FileInputStream fils = new FileInputStream(path);
//将文件输入流包装成缓冲流
BufferedInputStream fos=new BufferedInputStream(fils);
//读入字节
int a=fos.read();
while(a!=-1){
array[a]++;
//读取下一个字节
a=fos.read();
}
} catch (Exception ef) {
ef.printStackTrace();
}
return array;
}
建树的过程
/**
* 通过优先队列来构造哈夫曼树,返回根结点
*/
public TreeNode2 CreatTree(PriorityQueue<TreeNode2> list){
while(list.size()>1){
//取出两个构造
TreeNode2 node1=list.poll();
TreeNode2 node2=list.poll();
//创建父结点
TreeNode2 nodeparent=new TreeNode2();
nodeparent.sum=node1.sum+node2.sum;
list.add(nodeparent);
//建立关系
nodeparent.lchild=node1;
node1.parent=nodeparent;
nodeparent.rchild=node2;
node2.parent=nodeparent;
//给结点附哈夫曼编码
node1.flag1=0;
node2.flag1=1;
}
TreeNode2 lastnode=list.poll();
return lastnode;
}
这里传入的参数优先队列,因为系统的优先队列不能排序根结点,所以需要自己重写其中
的比较方法。
/**
* 将读入的数据存入优先队列
*/
public PriorityQueue<TreeNode2> arraylist(int array[]){
//根据指定的比较器创建一个优先队列
PriorityQueue<TreeNode2> list = new PriorityQueue<TreeNode2>(11,new MyComparator());
//将一个个结点对象存入到优先队列中
for(int i=0;i<array.length;i++){
if(array[i]!=0){
TreeNode2 treenode=new TreeNode2();
treenode.obj=i;
treenode.sum=array[i];
list.add(treenode);
}
}
return list;
}
/**
* 写一个类来是实现迭代器这个接口
* @author Administrator
*重写里面的方法compare
*/
class MyComparator implements Comparator<TreeNode2>{
public int compare(TreeNode2 node1,TreeNode2 node2){
int o1=node1.sum;
int o2=node2.sum;
return o1-o2;
}
}
2.通过哈夫曼树进行编码
/**
*
* @param str对应一个节点的哈夫曼编码
* @param root传入的根结点
* @param code哈夫曼编码字符串数组
*/
public void GetoneCode(String str,TreeNode2 root,String code[]){
//首先判断节点是否为空
if(root!=null){
if(root.flag1!=null){
str=str+root.flag1;
if(root.lchild==null&&root.rchild==null){
code[root.obj]=str;
System.out.println(root.obj+"该字节"+"哈夫曼编码为"+str);
//将找到的数据依次输入队列中
Data data=new Data();
data.str=str;
data.obj=root.obj;
list.add(data);
}
}
//递归遍历
TreeNode2 left=root.lchild;
GetoneCode(str,left,code);
TreeNode2 right=root.rchild;
GetoneCode(str,right,code);
}
}
3.利用哈夫曼编码对应的字节来进行读文件,然后把01字符串每8个字符串写成一个字节。
/**
* 文件压缩 并输出
* 将得到的字符串数组,取出字符串每8位变成一个字节
*/
public void OutPut(String path,String oldpath){
//记录文件中的01字符串的个数
int num=0;
//记录文件补0的情况
int addzero=0;
try{
//创建输入流对象
FileInputStream fis=new FileInputStream(oldpath);
//创建输入缓冲流
BufferedInputStream dis=new BufferedInputStream(fis);
//创建输出流对象
FileOutputStream fos=new FileOutputStream(path);
//创建输出缓冲流
BufferedOutputStream dos=new BufferedOutputStream(fos);
/**
* 写头文件,文件内容最后补零的情况
*/
dos.write(list.size());
System.out.println("队列的长度"+list.size());
for(int i=0;i<list.size();i++){
Data data=list.get(i);
dos.write(data.obj);
byte by[]=data.str.getBytes();
dos.write(by.length);
dos.write(by);
num+=array[data.obj]*data.str.length();
}
System.out.println("该数目为"+num);
addzero=8-num%8;//求出添零的个数
dos.write(addzero);
String writeString="";
byte[] by=new byte[(num+addzero)/8];//将变成的字节存在创建的byte数组
System.out.println("数组的长度为"+by.length);
int count=0;
while(dis.available()>0){
int i=dis.read();
//寻找与之匹配的编码
for(int j=0;j<list.size();j++){
boolean bool=false;//标记是否找到
if(list.get(j).obj==i){
int x=0;
while(true){
writeString+=list.get(j).str.charAt(x)+"";
x++;
//如果字符串长度已经达到8位
if(writeString.length()==8){
byte wri=this.change(writeString);
by[count]=wri;//将转化的字节保存到数组中
count++;
writeString="";//将字符串重新初始化
}
//如果字符串长度不足8位但是已经编码完全加入字符串中
if(x==list.get(j).str.length()){
bool=true;
break;//跳出循环
}
}
}
//如果找到,则跳出循环
if(bool==true){
break;
}
}
}
System.out.println("此时的count为"+count);
//如果还有剩余的字符在字符串中,则需要补0
if(writeString.length()>0){
int n=8-writeString.length();
for(int i=0;i<n;i++){
writeString+="0";
}
byte wri=this.change(writeString);//将8位转换为一个byte
by[count]=wri;//写入byte数组
}
//此处的写入的范围为多少
// dos.write(by.length);//写入byte数组的大小
dos.write(by);//写入byte数组
System.out.println("byte的数组长度为"+by.length);
dis.close();//关闭输入流
dos.close();//强制输出
fos.close();//关闭输出流
}catch(Exception e){
e.printStackTrace();
}
}
4.文件的解压
/**
* 将压缩的文件进行解压
*
*/
public void read(String path,String Newpath){
//创建存放数据的队列
ArrayList<Data> list1=new ArrayList<Data>();
try{
//创建输入流
FileInputStream fis=new FileInputStream(path);
//创建输入缓冲流
BufferedInputStream dis=new BufferedInputStream(fis);
//创建输出流
FileOutputStream fos=new FileOutputStream(Newpath);
//创建输出缓冲流
BufferedOutputStream dos=new BufferedOutputStream(fos);
/**
* 读头文件
*/
int size=dis.read();
System.out.println("队列的长度为"+size);
for(int i=0;i<size;i++){
Data data=new Data();
data.obj=dis.read();
int len=dis.read();
byte[] by=new byte[len];
dis.read(by);
data.str=new String(by);
list1.add(data);
}
int addzero=dis.read();//读出补0的情况
// int count=dis.read();//读出byte数组大小
int count=dis.available();
byte[] b=new byte[count];
System.out.println("补零数为"+addzero+"byte数组的大小为"+count);
dis.read(b);//读出byte数组
String str=new String("");
for(int i=0;i<b.length;i++){
int n=(int)b[i];
if(n<0){
n+=256;
}
str=str+this.changeInt(n);
}
//减去01字符串中末尾多加上的0
str=str.substring(0, str.length()-addzero);
boolean bool=true;
while(bool){
bool=false;
//寻找与之对应的字符
for(int i=0;i<list1.size();i++){
if(str.startsWith(list1.get(i).str)){
dos.write((int)list1.get(i).obj);
str=str.substring(list1.get(i).str.length());
bool=true;
break;
}
}
}
//关闭输入输出流
fis.close();
dos.flush();
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
看上去代码的确是很长,其中有些语句,没有深刻理解的话,确实难以弄懂。当然这个压缩也存在居多问题,比如压缩的文件大小太大,否则会卡机。可能是这个程序特别耗cpu导致的。
发表评论
-
自定义的ArrayList队列实现方法
2014-04-13 11:38 1395最近参加金山网络的一次笔试,给我感觉是基础需 ... -
n皇后问题
2013-11-03 22:53 1185之前听过一个 ... -
java里的反射机制
2013-05-19 10:53 722作为一个java初学者,想了解java里的反射机 ... -
关于优先队列和hash的简介
2013-05-04 11:50 1086关于优先队列和hash的简介 一.优先队列的引入 JDK ... -
Bitmap在排重问题上的应用
2013-05-04 11:42 878其实这篇,我已经写了好久,只是一直没发。因为里面还有一些 ... -
关于链表的一些基本操作
2012-09-24 22:53 944今天匆忙之中就快速展示我的链表一些基本操作,包括增加,删除, ... -
画图板的两种重绘方法
2012-07-15 20:41 1469对于这两种重绘方法 ... -
关键字
2012-07-15 19:41 678对于关键字的总结 1.访问限定符 用来定义 类的 ... -
登陆界面小结
2012-07-07 11:22 844今天我对前不久所学进行自己一些小结 1.首先谈到Java程序 ... -
排序1
2012-07-06 19:12 645关于排序的总结 1.冒泡排序 首先看一段代码 pub ... -
创建画图板窗口
2012-06-02 18:03 726//创建一个画图板窗体 //引入类和接口 ... -
接口的讲解
2012-05-19 23:36 736首先谈一下关于类的分类吧。有class类和inter ... -
继承的解析
2012-05-19 22:33 7201.什么是继承呢? 在现实中我需要定义很多的类,而实际 ... -
类与面向对象
2012-05-19 21:37 6731.面向过程与面向对象的理解 面向过程是指做一件事的经过: ...
相关推荐
mypage文件可能包含了实现哈夫曼压缩和解压缩算法的C源代码文件,以及相关的测试数据或结果。通过阅读和理解这些代码,你可以深入学习哈夫曼编码的工作原理,以及如何在C语言中实现这一算法。同时,还可以了解到如何...
总之,哈夫曼压缩解压缩是数据压缩领域的一个重要方法,通过MFC和VC++,我们可以构建出直观且高效的压缩工具,便于学习和研究。在这个过程中,理解哈夫曼编码的原理、掌握MFC的使用以及实现压缩和解压缩的算法,都是...
哈夫曼编码是一种高效的数据压缩编码技术,由美国科学家大卫·哈夫曼在1952年提出。这种编码方式是基于信源符号的概率分布,通过构建最优的二叉树来实现,使得出现频率高的符号拥有较短的编码,而出现频率低的符号...
根据提供的代码片段,我们可以进一步理解哈夫曼压缩的具体实现步骤: 1. **数据结构定义**: - `struct head` 定义了哈夫曼树中节点的数据结构,包括节点对应的字符(`b`)、出现频率(`count`)、父节点索引(`parent`...
在Java中实现哈夫曼压缩涉及到的主要步骤包括统计字节频率、构建哈夫曼树以及生成哈夫曼编码。首先,我们需要创建一个字节类(`NodeData`)来表示每个字节及其对应的权重(频率)。下面我们将详细讲解这些步骤: 1....
哈夫曼编码是一种高效的数据压缩方法,由大卫·哈夫曼在1952年提出。它是基于一种称为哈夫曼树(也叫最优二叉树)的数据结构。在这个课程设计中,你将深入理解哈夫曼编码的原理,并通过C++编程语言实现文件的压缩与...
在MATLAB中,哈弗曼编码通常包括以下几个步骤: 1. **频率统计**:首先,我们需要计算输入图片(如`lena.jpg`)中每个像素颜色值出现的频率。MATLAB中可以使用`imhist`函数来获取像素的直方图,这将帮助我们了解...
在Java编程环境中实现哈夫曼压缩与解压缩程序,我们可以利用面向对象的特性,设计多个类来完成不同部分的功能。 1. **FileChooserDemo.java**:这是一个用户界面类,通常包含用于让用户选择输入和输出文件的控件。...
在"哈夫曼压缩.zip"这个压缩包中,我们可以预见到包含了一个与哈夫曼压缩相关的项目。这个项目可能是用Visual Studio(VS)开发环境编写的,这意味着它可能是一个C++、C#或Visual Basic等编程语言的实现。实习性质...
在C++中实现哈夫曼压缩和解压,主要涉及到数据结构(如优先队列、二叉树)和文件操作(读写)。`huffmain`可能是这个C++项目的主程序文件,其中可能包含了构建哈夫曼树、生成编码、压缩和解压等核心功能的实现。具体...
在Java中实现哈夫曼压缩通常包括以下几个关键步骤: 1. **构建哈夫曼树**:首先,需要统计输入文本中每个字符出现的频率。然后,根据这些频率创建一个哈夫曼树。哈夫曼树是一种特殊的二叉树,其特点是叶子节点代表...
在Java中实现哈夫曼压缩涉及到以下几个关键步骤: 1. **统计字符频率**:首先,需要遍历输入文本,统计每个字符出现的次数,生成一个字符频率表。这是构建哈夫曼树的基础。 2. **构建哈夫曼树**:使用字符频率表,...
在实现哈夫曼树压缩算法的过程中,通常包括以下几个步骤: 1. **频率统计**:首先,对输入的数据流进行频率统计,得到每个符号(如字符)出现的次数。这一步骤有助于了解哪些符号更常见,哪些更不常见。 2. **构建...
优化的哈夫曼压缩步骤如下: 1. **频率统计**:首先,对输入数据流中的每个字符进行频率统计,得到每个字符出现的次数。 2. **构建哈夫曼树**:使用频率统计结果,通过贪心算法构建哈夫曼树。树的每个内部节点表示...
- 重复此步骤,每次选择频率最小的两个节点合并,直到只剩下一个节点,即为哈夫曼树的根节点。 3. **哈夫曼编码生成**: - 对哈夫曼树进行遍历,从根节点到叶节点的路径确定了字符的编码。左分支代表0,右分支...
1. **算法介绍**:详细解释哈夫曼压缩的基本原理和步骤。 2. **设计思路**:描述代码实现的具体策略和考虑,如如何优化效率,处理边界情况等。 3. **实现细节**:列出关键函数的实现,可能包括频率统计函数、哈夫曼...
在Java中实现哈夫曼编码压缩和解压,你需要以下几个关键步骤: 1. **编码阶段**: - 实现哈夫曼树的构建算法,如上述描述。 - 为每个字符生成哈夫曼编码,并存储在一个映射表中,以便后续解码使用。 - 遍历原始...
本文将详细介绍哈夫曼编码的基本原理、实现步骤以及如何将其应用于文本文件的压缩。 #### 二、哈夫曼编码算法 ##### 1. 基本原理 哈夫曼编码是一种基于统计的无损数据压缩算法,它通过构建哈夫曼树来实现对字符的...
实验步骤: 1、压缩 (1) 统计原始文件中各字节出现的概率(次数); (2) 采用哈弗曼算法对各字节进行编码,建立哈弗曼对照表; a) 构造二叉树 b) 编码 (3) 依次读取原始文件的每个字节,查找其对应的哈弗曼...
压缩过程主要包括以下步骤: 1. **统计频率**:首先,程序读取待压缩文件(如txt和doc文件),统计每个字符或字节的出现频率。 2. **构建哈夫曼树**:根据频率构建哈夫曼树,频率高的字符对应较短的编码,频率低的...