继续前文"哈夫曼压缩与解压缩学习笔记(二)"
http://128kj.iteye.com/blog/1706818
下面两个类主要是根据字节计数器的统计结果,创建哈夫曼树,计算字节的哈夫曼编码及由哈夫曼编码在树中找节点,往文件中写码表,读码表.
(5)HuffNode.java
//哈夫曼树节点
public class HuffNode implements Comparable<HuffNode>
{
public int value;//节点的字节值
public int weight;//节点的权值(字节出现的频率)
public int compareTo( HuffNode rhs )
{
return weight - rhs.weight;
}
HuffNode left;//左节点
HuffNode right;//右节点
HuffNode parent;//父节点
HuffNode( int v, int w, HuffNode lt, HuffNode rt, HuffNode pt )
{
value = v; weight = w; left = lt; right = rt; parent = pt;
}
}
(6)HuffmanTree.java
//哈夫曼树
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.PriorityQueue;
import java.io.*;
public class HuffmanTree {
public long charCountSum;
private CharCounter theCounts;//字节计数器
//存放哈夫曼树的所有节点,字节i对应的节点是theOndes[i]
private HuffNode [ ] theNodes = new HuffNode[ BitUtils.DIFF_BYTES + 1 ];//256+1
private HuffNode root;//哈夫曼树的根节点
public HuffmanTree( )
{
theCounts = new CharCounter( );
root = null;
}
public HuffmanTree( CharCounter cc )//构造函数
{
theCounts = cc;//注入字节计数器
root = null;
createTree( );//创建哈夫曼树
charCountSum=charCountSum();//总的字节数(原文件的大小)
}
public static final int ERROR = -3;
public static final int INCOMPLETE_CODE = -2;
public static final int END = BitUtils.DIFF_BYTES;
//根据字节计数器的统计结果,创建哈夫曼树,
private void createTree( )
{
PriorityQueue<HuffNode> pq = new PriorityQueue<HuffNode>( );//优先队列
for( int i = 0; i < BitUtils.DIFF_BYTES; i++ )//对0---256之间的每一个字节构造哈夫曼节点
if ( theCounts.getCount( i ) > 0 )
{
//以字节值,字节在原文件中出现的次数(权)构建哈夫曼节点
HuffNode newNode = new HuffNode( i,
theCounts.getCount( i ), null, null, null );
theNodes[ i ] = newNode;//保存节点
pq.add( newNode );//节点加入优先队列
}
while( pq.size( ) > 1 )//创建哈夫曼树
{
HuffNode n1 = pq.remove( );
HuffNode n2 = pq.remove( );
HuffNode result = new HuffNode( INCOMPLETE_CODE,
n1.weight + n2.weight, n1, n2, null );
n1.parent = n2.parent = result;
pq.add( result );
}
root = pq.element( );//返回哈夫曼树的根
}
public int[] getCode( int ch )//以整型数组的形式返回字节ch的哈夫曼编码,如{1,1,0},{1,1,1}
{
HuffNode current = theNodes[ ch ];//找到与ch对应的节点
if( current == null )
return null;
String v = "";
HuffNode par = current.parent;//ch的直接父节点
while ( par != null )//找ch的父节点,一直到根
{
if( par.left == current )//顺带写出ch的哈夫曼码
v = "0" + v;
else
v = "1" + v;
current = current.parent;
par = current.parent;
}
int [ ] result = new int[ v.length( ) ];
for( int i = 0; i < result.length; i++ )
result[ i ] = v.charAt( i ) == '0' ? 0 : 1;
return result;//以整型数组的形式返回字节ch的哈夫曼编码
}
public int getChar( String code )//由字节的哈夫曼编码求出此字节
{
HuffNode p = root;
for( int i = 0; p != null && i < code.length( ); i++ )
if( code.charAt( i ) == '0' )
p = p.left;
else
p = p.right;
if( p == null )
return ERROR;
return p.value;
}
//解压缩的关键方法
//从BitInputStream流中一位一位的读(然后在树中找节点),直到读到一个哈夫曼编码,返回哈夫曼编码对应的字节值
public int getChar(BitInputStream bips) throws IOException
{
HuffNode p = root;//哈夫曼树的根节点
while( p!=null)
{
int bit=bips.readBit();//从流中读一位
if(bit==-1)
return -1;
if(bit == 0 )//如果是0,在哈夫曼树中向左找
p =p.left;
else
p =p.right;//如果是1,在哈夫曼树中向右找
if(p.left==null&&p.right==null)
break;
}
if( p == null )
return ERROR;
return p.value;
}
//往压缩文件中写编码表信息:字节----字节的权值(出现的次数)
public void writeEncodingTable( DataOutputStream out ) throws IOException
{
for( int i = 0; i < BitUtils.DIFF_BYTES; i++ )
{
if( theCounts.getCount( i ) > 0 )
{
out.writeByte( i );//写字节
out.writeInt( theCounts.getCount( i ) );//写字节出现的次数
}
}
out.writeByte( 0 );
out.writeInt( 0 );//写一个结束标志
}
//读哈夫曼编码表
public void readEncodingTable( DataInputStream in ) throws IOException
{
for( int i = 0; i < BitUtils.DIFF_BYTES; i++ )
theCounts.setCount( i, 0 );//初值全部为0
int ch;
int num;
for( ; ; )
{
ch = in.readByte( );//读字节值
num = in.readInt( );//字节的权值
if( num == 0 )//遇到结束标志
break;
theCounts.setCount( ch, num );//将读到的结果放入字节计数器
}
createTree( );//根据字节计数器中的数据,创建哈夫曼树
charCountSum=charCountSum();//原文件总的字节数
}
public long charCountSum()//计算原文件总的字节数
{
return theCounts.charCountSum();
}
}
最后一个类,完成压缩和解压缩操作
import java.io.*;
import java.util.Calendar;
public class Hzip {
//压缩文件
public static void compress( String inFile ) throws IOException {
//读取数据文件
FileInputStream fis =new FileInputStream(inFile);//要压缩的文件
CharCounter charCounter=new CharCounter(fis);//字节计数器,统计文件中字节出现的次数
HuffmanTree tree=new HuffmanTree(charCounter);//根据字节计数器统计的结果创建哈夫曼树,得到根节点
fis.close();
String compressedFile = inFile + ".huf";//压缩文件的扩展名为huf
OutputStream fout =null;
fout=new FileOutputStream( compressedFile );//准备要写的压缩文件
DataOutputStream out=new DataOutputStream(fout);
tree.writeEncodingTable(out);//往压缩文件中写入码表(字节--出现次数)信息,以便解压缩时恢复哈夫曼树
fis=new FileInputStream(inFile);//原文件
BitOutputStream hzout = new BitOutputStream(fout );//压缩后的文件
int ch;
while( ( ch = fis.read( ) ) != -1 )//从原文件中读一个一个字节地读
{
hzout.writeBits( tree.getCode(ch) );//写入这个字节到压缩文件,八位八位地写
}
fis.close( );
hzout.close( );
fout.close();
}
//解压缩文件
public static void uncompress( String compressedFile ) throws IOException{
String outFile;
String extension;
outFile = compressedFile.substring( 0, compressedFile.length( ) - 4 );
extension = compressedFile.substring( compressedFile.length( ) - 4 );
if( !extension.equals( ".huf" ) )//先判断要解压文件的扩展名
{
System.out.println( "Not a compressed file!" );
return;
}
InputStream fin = new FileInputStream(compressedFile );//准备读
DataInputStream in = new DataInputStream( fin );
HuffmanTree tree=new HuffmanTree();
//先从已压缩文件中读出码表(字节---字节出现的次数)信息,并由此信息创建哈夫曼树,
//具体代码请参看tree.readEncodingTable(in)方法
tree.readEncodingTable(in);
System.out.println("总频数:"+tree.charCountSum);
//解码,开始解码压缩文件
BitInputStream hzin = new BitInputStream( in );
OutputStream fout = new FileOutputStream( outFile );//准备输出文件
int ch;
long count=0;
//tree.getChar(hzin)方法从BitInputStream流中一位一位的读(然后在树中找节点),
//直到读到一个哈夫曼编码,返回此哈夫曼编码对应的字节值
while(( ch =tree.getChar(hzin) ) != -1 )
{
fout.write(ch );//将解压后的字节写入输出文件
count++;
if(count>=tree.charCountSum)
break;
}
fin.close();
hzin.close( );
fout.close( );
}
//大家测试吧
public static void main( String [ ] args ) throws IOException
{
String cf="D:\\temp\\7.bmp";
long start = System.currentTimeMillis();
//compress(cf);
uncompress(cf+".huf");
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end-start)+ " 毫秒");
}
public static void main2( String [ ] args ) throws IOException
{
if( args.length < 2 )
{
System.out.println( "Usage: java Hzip -[cu] files" );
return;
}
String option = args[ 0 ];
for( int i = 1; i < args.length; i++ )
{
String nextFile = args[ i ];
if( option.equals( "-c" ) )
compress( nextFile );
else if( option.equals( "-u" ) )
uncompress( nextFile );
else
{
System.out.println( "Usage: java Hzip -[cu] files" );
return;
}
}
}
}
全文完。下载源码:
分享到:
相关推荐
5. **压缩与解压缩流程**:在压缩过程中,原始文件先被分块,然后对每个块应用Huffman编码,生成的二进制流写入新的文件。解压缩则需要逆向执行这个过程,读取压缩文件中的二进制流,解码成原始的字符序列,最后重建...
- **哈夫曼树**:压缩编码,如 poj3253。 - **堆**:用于优先队列和最值问题。 - **Trie 树**:高效存储和查找字符串,分为静态建树和动态建树。 4. **简单搜索**: - **深度优先搜索(DFS)**:poj2488、poj...
其他树结构包括平衡树(如AVL树和红黑树)以及哈夫曼树(用于数据压缩)。 3. **图数据结构**:图由节点(或顶点)和连接节点的边构成,可以表示复杂的关系。图可以是无向的(边没有方向)或有向的(边有方向)。图...
内容概要:本文详细介绍了应用于电镀生产线的西门子S7-300 PLC控制系统的程序设计、硬件配置以及调试过程中积累的实际经验。主要内容涵盖温度控制、条码记录、行车定位、故障排查等方面的技术细节。文中展示了多个关键功能模块的具体实现方法,如PID温度控制、条码数据处理、行车定位判断等,并分享了一些实用的调试技巧和注意事项。此外,还讨论了硬件配置中的重要细节,如模块地址分配、网络拓扑设计等。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是对PLC编程有一定基础的人群。 使用场景及目标:适用于需要深入了解和掌握电镀生产线自动化控制技术的专业人士。目标是帮助读者理解S7-300 PLC在电镀生产线中的具体应用,提高实际项目的开发效率和可靠性。 其他说明:文章不仅提供了详细的程序代码示例,还分享了许多来自一线的真实案例和实践经验,对于解决实际工程中的问题具有很高的参考价值。
内容概要:本文详细介绍了使用COMSOL Multiphysics进行固体超声导波的二维仿真过程。作者通过建立一个10mm×100mm的铝板模型,应用汉宁窗调制的5周期200kHz正弦激励信号,研究了超声导波在铝板中的传播特性及其模式转换现象。文中涵盖了从模型构建、材料参数设置、网格划分、边界条件设定、激励信号施加到求解设置以及结果分析的完整流程。特别强调了汉宁窗调制的作用,即减少频谱泄漏并提高信号质量。 适合人群:从事超声检测、材料科学、物理学等相关领域的研究人员和技术人员,尤其是那些希望深入了解COMSOL仿真工具及其在超声导波研究中应用的人群。 使用场景及目标:适用于需要精确模拟超声波在固体介质中传播的研究项目,旨在验证理论预测、优化实验设计、评估不同材料和结构对超声波的影响。此外,还可以用于教学目的,帮助学生掌握COMSOL软件的操作方法和超声导波的基础知识。 其他说明:文中提供了详细的参数设置指导和代码片段,有助于读者快速复现仿真过程。同时,作者分享了一些实用技巧,如如何正确设置网格大小、选择合适的窗函数等,以确保仿真结果的准确性。
离职人员分析仪表盘.xlsx
内容概要:本文详细介绍了如何利用LabVIEW搭建一个多功能的虚拟函数信号发生器及其信号分析功能。首先,文章展示了如何通过LabVIEW的前面板和程序框图创建各种常见波形(如正弦波、方波、三角波等),并深入探讨了波形生成的具体实现方法,包括三角波的周期性和斜率计算、白噪声的生成以及自定义公式的解析。接着,文章讨论了信号处理的关键技术,如自相关分析、频谱分析、积分和微分运算,并提供了具体的实现代码和注意事项。此外,文中还分享了一些实用的经验和技术细节,如避免频谱泄漏的方法、处理多频波的技术、防止内存泄漏的措施等。 适用人群:从事信号处理、电子工程、自动化控制等领域的工作技术人员,尤其是那些熟悉或希望学习LabVIEW编程的人士。 使用场景及目标:适用于实验室环境或教学环境中,用于替代传统物理信号发生器进行信号生成和分析实验。主要目标是提高信号生成和分析的灵活性和便捷性,减少对昂贵硬件设备的依赖。 其他说明:本文不仅提供了详细的代码示例,还分享了许多作者在实践中积累的经验教训,帮助读者更好地理解和应用LabVIEW进行信号处理。
线性代数
大雾至尊版V56泛滥无密码.zip
员工生日关怀方案
试用期情况跟踪表.xls
员工激励机制与技巧
员工晋升的自我评价.doc
基于51单片机protues仿真的多功能婴儿车控制器(仿真图、源代码、AD原理图) 该设计为51单片机protues仿真的多功能婴儿车控制器,实现温湿度,音乐,避障,声音监测控制; 1、温湿度检测,婴儿尿湿时会有提醒。 2、声音检测,当婴儿啼哭时也会有提醒。 3、小车避障,小车遇到障碍会后退左转。 4、音乐播放。 5、仿真图、源代码、AD原理图;
内容概要:本文档详细介绍了计算机求职笔试的内容与解答,涵盖编程语言基础、数据结构与算法、编程实践与调试、系统设计与软件工程以及综合题型与开放题五个方面。编程语言基础部分强调了语法规则、数据类型与运算符、面向对象编程的核心概念;数据结构与算法部分讲解了常见数据结构(如线性结构、树与图、哈希表)和高频算法(如排序算法、动态规划、递归与回溯);编程实践与调试部分关注编码能力和调试技巧;系统设计与软件工程部分探讨了设计模式、模块化设计、数据库与网络知识;综合题型与开放题部分则提供了场景题和逻辑思维题的示例。最后给出了备考建议,包括知识体系构建、刷题策略和模拟实战的方法。 适合人群:即将参加计算机相关职位笔试的求职者,特别是对编程语言、数据结构、算法设计有初步了解的应届毕业生或初级工程师。 使用场景及目标:①帮助求职者系统复习计算机基础知识,提升笔试通过率;②通过例题和解答加深对编程语言、数据结构、算法的理解;③提供模拟实战环境,提高时间管理和抗压能力。 阅读建议:建议按照文档提供的知识体系顺序进行系统复习,重点攻克高频题型,利用在线平台刷题练习,并结合实际项目经验进行综合应用,同时注意时间管理和抗压能力的训练。
SecureCRT安装包
物流业人才流失与紧缺现象的对策研究
招聘渠道费用仪表盘P10.pptx
内容概要:本文详细介绍了五相永磁同步电机在Simulink环境下的PI双闭环SVPWM矢量控制建模过程及其优化方法。首先阐述了五相电机相比三相电机的优势,如更小的转矩脉动和更强的容错能力。接着探讨了复杂的Simulink模型搭建,涉及电机本体模块、坐标变换模块、SVPWM模块和PI调节器模块等多个组件。文中提供了具体的Clark变换和PI调节器的代码示例,解释了双闭环控制的工作原理,并详细描述了SVPWM与十扇区划分的具体实现方式。最后展示了模型的性能表现,包括良好的波形质量和快速的动态响应特性。 适合人群:从事电机控制领域的研究人员和技术人员,尤其是对五相永磁同步电机和Simulink建模感兴趣的读者。 使用场景及目标:适用于希望深入了解五相永磁同步电机控制原理并掌握具体实现方法的研究人员和技术人员。目标是帮助读者理解五相电机的特殊性和复杂性,掌握PI双闭环SVPWM矢量控制的建模技巧,提高电机控制系统的设计水平。 其他说明:文章不仅提供了理论知识,还包括了大量的代码片段和实践经验分享,有助于读者更好地理解和应用相关技术。
员工离职交接表-模板.doc