a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 356|回复: 0

[专业语言] Java认证:使用哈弗曼编码实现压缩软件

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
Java认证:使用哈弗曼编码实现压缩软件$ }( c1 [& ^" }- V& S
使用哈弗曼编码实现压缩软件# P; [4 _0 y5 d. F, Z
1.什么是哈夫曼树?. q( b# G/ ?- Q8 Q7 e: {9 g! C
哈夫曼树是一种最优二叉树,它的最优点体现在它的的带权路径长度最小。(结点的带权路径长度为:结点的路径长度乘以结点的权值,树的带权路径长度为所有叶子结点带权路径长度之和)
1 G' J5 u3 o% E2 [2.什么是哈弗曼编码?
* r# i# D7 }3 g4 d( a8 H- n: ]7 |从哈弗曼树的根结点开始,按照左子树分配代码“0”,右子树分配代码“1”的规则,直到叶子结点为止,每个叶子结点的哈弗曼编码就是从根结点开始,一直到该叶子结点为止,把途中经过的代码按顺序串起来就OK了。
( W2 ?. j* _3 n( T, m3.什么是哈弗曼压缩?  Z$ {8 x( y/ F+ C- u: }
Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压缩方法,在压缩过程中不会丢失信息熵,而且可以证明 Huffman 算法在无损压缩算法中是最优的。 Huffman 原理简单,实现起来也不困难,在现在的主流压缩软件得到了广泛的应用。对应用程序、重要资料等绝对不允许信息丢失的压缩场合, Huffman 算法是非常好的选择。' `) R2 Z7 q( z4 g/ j# S3 I( r  O: _
哈夫曼压缩,一般用来压缩文本和程序文件。哈夫曼压缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
* v: k! f  Z) Q8 H4 r, o+ C9 O有了以上的基本概念,我们就可以动手实现哈弗曼压缩软件了!( ]8 M) z) s  X* D- N! e5 d0 \: `
4.哈弗曼压缩软件的实现:
6 N5 V/ X) x& V) y在文件中,所有的数据都是以字节的形式存在的,一个字节是8位,所以所有可能出现的字节在0——256之间(不包括256),所以,第一步要做的事情就是,把文件所有的字节的出现频率统计出来,并用频率做为权值生成一棵哈弗曼树:* m4 ^3 v) v) X) R# X
Java代码3 a" `  U. b9 X& T
int[] ByteCount = new int[256];//字节频率统计
! `2 B7 K* w) P2 m4 y! ^InputStream fis = new FileInputStream(pathName_former);
+ F# _) F9 r: U1 h* OInputStream bis = new BufferedInputStream(fis);//创建文件输入流
  {6 m- y5 p# a9 y6 Ewhile(bis.available()》0){' O" ?5 n! ^  N7 l9 E- o' p* p
int tmp = bis.read();
$ C& [* O2 z7 v# sByteCount[tmp]++;0 e  _  b$ t9 \* ~+ [
}//统计频率$ D" |+ {/ U6 e3 i: }  {/ M
//构造哈弗曼树
# B! ~! t1 L9 ahfmTree hfm = new hfmTree(ByteCount);. L+ u" k: m0 `: ?
Java代码$ D1 @+ [5 [; K7 y) W5 B5 X
public hfmTree(int[] rank){//根据频率构造哈弗曼树
: Z5 Y* A% v* |, T9 _; Y//把频率不为零的字节加入节点优先级队列3 ^' s& X4 x4 g7 B' _$ E; C
PriorityQueue nodes = new java.util.PriorityQueue();
. L1 E) U8 I9 P6 M) Ifor(int i=0;i
8 V4 ~' a$ s/ k: \if(rank[i]!=0){
* b' m* t0 }: Anodes.add(new hfmNode(rank[i],i));% C; V! G2 X5 n  i$ K( ?; c
}5 R+ E6 {8 B: a* Z6 v& N0 O- c  A0 Z
}
# v: m& |+ k; G2 h/ y8 {//构造哈弗曼树, q) o4 A/ f2 u+ M
while(nodes.size()!=1){
4 H3 |; i- H. p7 hhfmNode node_1 = nodes.poll();//10 B& P6 ^8 N( Z" v% n
hfmNode node_2 = nodes.poll();//2" m% e3 P' f* A5 e& {0 d2 r
hfmNode addNode = new hfmNode(node_1.getRank()+node_2.getRank(),0);
, L" p1 T9 n( f* @9 \& T; {addNode.setLeft(node_1);! H7 N: `& U# U7 I" \5 i2 c* F1 m
addNode.setRight(node_2);
1 C2 |0 ^/ k$ q8 |* |1 a; N5 Pnodes.add(addNode);# K9 c3 n% L4 q
}9 N! C" ?6 c8 J

% h2 h8 H7 J3 {/ F* Z
# e2 d0 K9 g+ L7 y) s1 sroot = nodes.poll();; Y# Q, U6 m" k/ k
}//构造函数(correct)
. i& H4 d9 |- U; Y, L接下来,要做的就是获得0——256之间每个字节所对应的哈弗曼编码,用一个String[] Code = new Code[256]保存 下来Java代码//获得编码: Y' E. m: O, h! Y/ j* v5 B
private void getStrByte(hfmNode node,String s){
5 O2 `& t4 r' u9 Zif(node.getLeft()==null&&node.getRight()==null){
! _* \- U( z! |Code[node.getData()] = s;//获得编码字符串5 j& L% O( g" X" T' d- n. G
}" n" k$ O5 S# l2 \* {
if(node.getLeft()!=null){
/ i! X3 q: X$ t; y4 jgetStrByte(node.getLeft(),s+“0”);
8 s$ [- Z/ r* w6 r}//左零 if(node.getRight()!=null){
; j2 b: k9 H% G7 `( S! B) ygetStrByte(node.getRight(),s+“1”);1 a/ R7 ~% i" ~" [0 D% D" D# y
}//右14 h* m7 \2 j8 ~8 S
}
2 A1 l( h2 r' ]- s8 T0 g  h; A然后就是把每个字节的编码长度打入文件:Java代码//先将0-255的编码长度打到文件里! k$ |, G3 v( O# [7 y9 H2 u
for(int i=0;i7 _& G, f4 e: B% k0 X
if(Code[i]==null||Code[i]==“”){/ M, H/ e* [' w  p1 @
Code[i] = “”;7 I! A4 l$ |; p! h1 k" U
bos.write(0);: D2 r$ r* _/ m1 s
}else{5 J1 Z( v  F1 _9 D  m
bos.write(Code[i].length());
% j# P- \( @$ m2 \7 U7 x}& K! H8 P+ W; \, l- V& x/ c! I
}
6 N8 a* s1 Z" I0 ~# y接下来就是,将每个字节的编码打入文件(这里需要吧8个长度的01串转换成一个byte打入文件):Java代码//把每个字节的编码表打到文件里
9 @2 f$ g, D; E' @9 Y. cint i = 0;//第i个字节. I* {, S' `" g! {$ f: z
int count = 0;//满8打一,计数器
0 V2 }5 \6 ^1 M7 d& q7 EString writeCode = “”;//写入的8位编码
9 I, w4 \' S( x7 O( j+ f, fString allCode = “”;//所有待写入的编码5 l6 g) g. ^1 m
String inCode = “”;
7 r! V, O% n3 E" _+ Twhile(i《256||count》=8){
6 }5 g- ?5 g2 l& N6 pif(count》=8){//满8& G6 Q( p! G( _6 T* U1 w
writeCode = allCode.substring(0,8);//前8位% @" q( B% {2 s. f/ `
count-=8;6 `1 U# k: V, H. A3 J
allCode = allCode.substring(8);% u4 l* {, Q$ [% a
bos.write(changeString(writeCode));//写入一个字节' M  G7 j/ H, f8 K' e
}else{$ l! q! `" t" C; x
count+=Code[i].length();# H1 J  Y4 j* A' e' ~( d
allCode+=Code[i];$ R9 g5 ]- k: G- M! Y% s
inCode+=Code[i];5 K% V5 e9 s' \* s
i++;//严重错误发生地% a  U  h, @8 T$ c( L
}
$ L! F, t% }! X; \6 z& `}' ]  y2 C& X7 Q) U7 d1 g
//如果不满8位的2 d2 d+ H6 m% \* H& n* ~: E* v
if(allCode.length()》0){, p2 {( j+ V2 N3 {- D% k, t
int len = 8-allCode.length();//补零9 X& U% o+ B* V4 y
for(int j=0;j1 k$ O( n; Y: D% x
allCode+=“0”;
; ]! ]5 H, S; ~4 V}
# {5 B4 w0 D! Y1 R3 |5 ninCode+=allCode;
# i9 @, p% V: I& R5 cbos.write(changeString(allCode));# Q2 ^  m( b) F/ t* W+ R' L
}1 b' K" d3 e& o: ]6 y
最后就是把文件中的所有字节按照这种哈弗曼的编码方式保存到文件中,过程类似于上一步(在最后打入末尾补入的0的个数,主要是为了方便解压缩):Java代码//编码表输出完毕,将文件中的字节按照这种编码方式压缩
, z1 q2 K, C6 @8 i2 Y3 S1 q3 z% CInputStream ins = new FileInputStream(pathName_former);/ l+ Q, h7 `+ [" Q
InputStream buf = new BufferedInputStream(ins);//创建输入流1 d* Q% _4 Q- s7 H; u/ B, K
count = 0;  c0 i$ ?* w0 y# ^/ z" M, {. H
writeCode = “”;) W9 v' T! t, o
allCode = “”;
6 s3 @" z6 Q8 h7 d. C8 ~# K0 `' k
* v! Q2 U0 S' d- d  k# M% y+ V5 m, k  }" I  s( Y6 a
while(buf.available()》0||count》=8){+ l, A  w9 ^1 S$ l
if(count》=8){//满8
  E: G, q7 g  v8 \& m. d% t* zwriteCode = allCode.substring(0,8);//前8位4 H  y  ~; D# v+ _) @5 W% H8 e  M
count-=8;
7 `; J. {% G; p9 PallCode = allCode.substring(8);
  d' e" p8 v) H3 k7 v3 U8 A6 T- X2 rbos.write(changeString(writeCode));//写入一个字节
# U3 J5 S& N; W2 d; p# {}else{' C1 A: S$ K- A/ M! O+ v' L+ K
int data = buf.read();/ j, x6 V8 ~( i1 z" {8 a
count+=Code[data].length();% d/ N1 v2 J% R2 a7 `, B1 f1 ^
allCode+=Code[data];
1 h9 F& B6 x7 e/ r: ^}9 f) i" }: W. m& Z' g0 T. c
}
5 h' T9 r4 A: i4 x2 ?; L; k" v4 q//如果不满8位的
& }; e+ g: A" ?; ^9 x8 n2 vif(allCode.length()》0){
0 t, F( R* f0 {8 k* J' c9 Vint len = 8-allCode.length();//补零( ^# Q5 D- }, W# b' O" U1 {
for(int j=0;j
4 V+ g4 ~# g4 b: |/ RallCode+=“0”;; o6 i% b; Q/ b6 E! m) _) @
}
) M1 g) Y. V$ a8 y. f' Tbos.write(changeString(allCode));
) Y0 `' w  h5 `  cbos.write(len);//补入了几个0
. Y% ]* c8 [4 N}else{5 e, n) ~/ V# ^' C5 y5 V
bos.write(0);//补入了0个0, ?) e; l3 o2 m- L9 g( p" z. k
}% _# L& ~) n) H* }8 [
到这里,整个压缩过程基本完成了,解压缩就是压缩的逆过程,在此不再赘述,所有的操作都保存在附上源代码中。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-26 05:00 , Processed in 0.210355 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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