a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 153|回复: 2

[其他] JAVA技巧:使用哈弗曼编码实现压缩软件

[复制链接]
发表于 2012-8-4 12:28:23 | 显示全部楼层 |阅读模式
使用哈弗曼编码实现压缩软件
( L( n, r" G# M1.什么是哈夫曼树?
% t) U% |) Z: U' J& c哈夫曼树是一种最优二叉树,它的最利益表此刻它的的带权路径长度最小。(结点的带权路径长度为:结点的路径长度乘以结点的权值,树的带权路径长度为所有叶子结点带权路径长度之和)
# |( n6 k7 e; h2 I: {& |: M3 |; I, T2.什么是哈弗曼编码?" E' D: C9 k, v9 }
从哈弗曼树的根结点起头,按照左子树分配代码“0”,右子树分配代码“1”的轨则,直到叶子结点为止,每个叶子结点的哈弗曼编码就是纲要结点起头,一向到该叶子结点为止,把途中经由的代码按挨次串起来就OK了。, A! s) U0 U% B9 Y! e4 w
3.什么是哈弗曼压缩?
% q! m8 N- V; X0 g$ y2 T) VHuffman( 哈夫曼 ) 算法在上世纪五十年月初提出来了,它是一种无损压缩体例,在压缩过程中不会丢失踪信息熵,而且可以证实 Huffman 算法在无损压缩算法中是最优的。 Huffman 道榔撇,实现起来也不坚苦,在此刻的主流压缩软件获得了普遍的应用。对应用轨范、主要资料等绝对不许可信息丢失踪的压缩场所, Huffman 算法长短常好的选择。. y8 O9 ]2 Q$ [, F) R) H; S. P1 e; |
哈夫曼压缩,一般用来压缩文本和轨范文件。哈夫曼压缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。是以,在文件中呈现频率高的符号,使用短的位序列,而那些很少呈现的符号,则用较长的位序列。) w9 v) Q& T( q; ^3 X/ |
有了以上的根基概念,我们就可以脱手实现哈弗曼压缩软件了!
; j. y, L. j; J: p4 E4 i2 u. x1 T4.哈弗曼压缩软件的实现:# [- s% E) |; g: Z; f6 i
在文件中,所有的数据都是以字节的形式存在的,一个字节是8位,所以所有可能呈现的字节在0——256之间(不搜罗256),所以,第一步要做的工作就是,把文件所有的字节的呈现频率统计出来,并用频率做为权值生成一棵哈弗曼树:3 q9 Z, i0 Z6 W' M; f9 X7 C% M. K  F
Java代码
( u/ A- A6 k) I5 @+ Y; _int[] ByteCount = new int[256];//字节频率统计
' o$ [& u% z" J% MInputStream fis = new FileInputStream(pathName_former);
  A9 C( X- Y0 t3 B8 WInputStream bis = new BufferedInputStream(fis);//建树文件输入流
1 i; o  v% s+ rwhile(bis.available()》0){  t1 x( V: w# V. ~
int tmp = bis.read();) K. M6 g$ O0 x( h% e) F& Z/ s  T
ByteCount[tmp]++;
2 R8 r: e" l2 Z) J5 N9 B}//统计频率
+ J! }: {$ p7 _$ B" r//机关哈弗曼树
7 K4 c& m/ Y+ j6 ZhfmTree hfm = new hfmTree(ByteCount);
9 E5 Y3 K" S9 i* xJava代码  T# O- Z8 C8 i/ k4 e9 n
public hfmTree(int[] rank){//按照频率机关哈弗曼树( u/ y) a4 w( _; F
//把频率不为零的字节插手节点优先级队列
  R) C6 U9 k3 W! [% y% e. S1 ePriorityQueue nodes = new java.util.PriorityQueue();
2 s: J/ H  ?" I* j( r9 Qfor(int i=0;i
* B2 ?! V0 l/ Z: _( Bif(rank[i]!=0){
. a+ p8 }4 t" m% f1 vnodes.add(new hfmNode(rank[i],i));9 z! ]# ~2 ~, z# r  H1 a
}
8 P- m$ B- e6 n9 \}& x6 @( P( N# {
//机关哈弗曼树) x# B( `6 K* _
while(nodes.size()!=1){
% ?7 p% i+ j% e' EhfmNode node_1 = nodes.poll();//1
3 c+ [9 e( g9 l, @" Q1 \& q. vhfmNode node_2 = nodes.poll();//2
* i1 r& Q1 @2 p" v1 y/ p& g, L0 `hfmNode addNode = new hfmNode(node_1.getRank()+node_2.getRank(),0);. o% l- p3 X+ r+ b0 @
addNode.setLeft(node_1);
* k( M3 S6 r8 {2 Y# A0 [addNode.setRight(node_2);
4 w( i' e" \/ [  U5 ^2 K; o1 @nodes.add(addNode);
5 V6 u% D3 t2 ~6 k' c6 f8 x
4 p6 R% X9 {3 a( S! v5 ~* I}
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:28:24 | 显示全部楼层

JAVA技巧:使用哈弗曼编码实现压缩软件

</p>2 a  r1 o" ^9 O
7 F; [- N& }  }# ?
root = nodes.poll();5 w. D: w: S9 K( h8 N
}//机关函数(correct)' B! D  D" v: G+ E. o/ z3 i/ }
接下来,要做的就是获得0——256之间每个字节所对应的哈弗曼编码,用一个String[] Code = new Code[256]保留 下来Java代码//获得编码$ e9 ]- r1 E" E
private void getStrByte(hfmNode node,String s){
7 J; o# E0 u+ g# l+ ~: }* R+ rif(node.getLeft()==null&&node.getRight()==null){$ `0 C% l4 Q# _0 w
Code[node.getData()] = s;//获得编码字符串
. N  l- J# J7 S9 m  {5 x7 T+ n}
7 T: @; z; \8 F* gif(node.getLeft()!=null){
/ _; O5 B" |  c5 ngetStrByte(node.getLeft(),s+“0”);1 \3 {; o2 r& ?
}//左零 if(node.getRight()!=null){
" e5 u5 o: X* m9 b6 ]1 {% @3 Q' m4 ?getStrByte(node.getRight(),s+“1”);
4 z5 C. h3 E( x/ W) K. c}//右1
, K- i  Q- S. I, L) W! M, E}
1 p$ {  g7 r) i6 z  y0 q% R9 A/ k然后就是把每个字节的编码长度打入文件:Java代码//先将0-255的编码长度打到文件里
% I8 n+ C& f$ D4 p) J) E/ f& v& Efor(int i=0;i6 u- I& p$ I- n
if(Code[i]==null||Code[i]==“”){: e6 z5 a7 H& I* a" k
Code[i] = “”;
; B6 Y+ {* p& N8 D$ ]; lbos.write(0);
7 Y' o2 f& x- ^+ f/ N* ]" ~}else{
, r! x. R( S8 sbos.write(Code[i].length());$ P- J5 f! `% r( u# {
}  T6 J& F9 H3 t) a% s& q$ l
}) p* P; r) M- Y8 f& h( t  W- \6 S
接下来就是,将每个字节的编码打入文件(这里需要吧8个长度的01串转换成一个byte打入文件):Java代码//把每个字节的编码表打到文件里2 A4 D" s; g1 L' _) ?( n3 l
int i = 0;//第i个字节
9 Y6 Y# j- \  Z% H6 C+ Dint count = 0;//满8打一,计数器2 j! H, U& Q+ v
String writeCode = “”;//写入的8位编码
1 V# T8 V: S6 R; ~, f& bString allCode = “”;//所有待写入的编码0 ~7 a3 N/ X3 P; L; ~7 N1 ]) {
String inCode = “”;) }3 E6 c, V' V3 h+ N' O( D0 x; {) P
while(i《256||count》=8){
1 H3 G7 V7 Y5 p2 y, Kif(count》=8){//满8; w3 ?. W0 ^4 m% C6 _8 }* a
writeCode = allCode.substring(0,8);//前8位
8 g9 |- r2 N3 v  G8 wcount-=8;
# _) o9 O& ?( s# V' h  i1 h8 T" jallCode = allCode.substring(8);
9 N$ M7 Y( p& q% M& A1 `2 F  z: \bos.write(changeString(writeCode));//写入一个字节* A0 F5 l/ v2 E+ J. J" F
}else{3 T2 Y% q% o& h9 M- G
count+=Code[i].length();4 S- {# X! z) {# l% x6 c5 c$ h
allCode+=Code[i];
# d: ~' t1 h# S4 G. v9 H/ c/ I3 VinCode+=Code[i];
9 A: [4 a! Z5 I  I7 o% o4 U4 ii++;//严重错误发生地
0 d, C: C4 o5 h' E; P2 M3 n}
5 F7 @  D  g( j, S5 ]' n}. D/ @4 T' U! B' ~7 F
//如不美观不满8位的& z% n% u/ |  G' Q, u
if(allCode.length()》0){$ j1 x8 I0 p) Z- f1 H: Q
int len = 8-allCode.length();//补零
' N8 T' ]8 z* n- T: L4 y+ d& \/ e  B% qfor(int j=0;j$ Y0 B. f* c7 O. P! A0 [
allCode+=“0”;1 W4 h* I, y: ~

" j5 {- n- I6 j/ O" w6 a}
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:28:25 | 显示全部楼层

JAVA技巧:使用哈弗曼编码实现压缩软件

</p>inCode+=allCode;5 l. f2 o- r" k2 f8 K6 m& w3 S
bos.write(changeString(allCode));* t/ J9 v; M2 z1 v2 z
}' s% h& e3 o2 V1 Z7 g+ _
最后就是把文件中的所有字节按照这种哈弗曼的编码体例保留到文件中,过程近似于上一步(在最后打入末尾补入的0的个数,主若是为了便利解压缩):Java代码//编码表输出完毕,将文件中的字节按照这种编码体例压缩  W( b' A; Y5 P) f- w3 J& n6 M7 W4 G
InputStream ins = new FileInputStream(pathName_former);
* J2 C, n" k' t7 o4 LInputStream buf = new BufferedInputStream(ins);//建树输入流
7 g* b# \' T2 rcount = 0;
9 p- v& P, @9 f  B) Q8 LwriteCode = “”;
5 h. K3 R$ N; f& |2 DallCode = “”;
( ~0 J# l9 ?- Y& q5 `/ R) m/ u6 m8 h4 P7 e( ?, y

$ s( ]$ }+ ?4 s; ^0 V2 Iwhile(buf.available()》0||count》=8){) w/ ~$ [& q6 \' [: S- V
if(count》=8){//满8' T" w. G/ }2 f; v. V
writeCode = allCode.substring(0,8);//前8位3 {% [  o5 K" N6 f. V7 Q
count-=8;
  k0 v6 S5 l+ W3 g9 {+ H0 C# eallCode = allCode.substring(8);" E4 b3 }3 \" `; W. r$ }/ X
bos.write(changeString(writeCode));//写入一个字节' M2 P+ m8 {- o6 j. t
}else{3 J2 p9 s% H' h2 g# Q( H, y
int data = http://www.examw.com/java/zhuangye/157582/buf.read();
! D7 s% o2 _& u7 c9 F% qcount+=Code[data].length();2 l& U3 I# F( z1 d! \: P
allCode+=Code[data];
- v: P6 m! o" q# t( A6 x) x}: I! i1 D7 O: s4 _" ?( b: b7 F/ }" b& H
}$ v6 ~% w  u* R( y0 Q
//如不美观不满8位的
1 I* @  S9 A: s! Qif(allCode.length()》0){- y5 s0 X/ i; T1 w4 l
int len = 8-allCode.length();//补零1 C* y# b, w9 u% Z* O
for(int j=0;j
' X9 d' G0 u% w0 f$ m8 T, ~! Z3 WallCode+=“0”;6 f. J7 j( y, r/ z( J
}
- s, N& Q& m5 c$ nbos.write(changeString(allCode));4 u! L0 C# m+ ]. v) t1 B
bos.write(len);//补入了几个0
8 e, v+ c4 v8 q- F( T8 f- c}else{
) b. Z/ F" w7 dbos.write(0);//补入了0个0; J! w8 R' X' p; J( E" r
}( O5 Z; G' R) ^  y5 U/ g
到这里,整个压缩过程根基完成了,解压缩就是压缩的逆过程,在此不再赘述,所有的操作都保留在附上源代码中。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-4 04:30 , Processed in 0.257342 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表