哈夫曼编码(Huffman)Java实现代码简化版
生活随笔
收集整理的這篇文章主要介紹了
哈夫曼编码(Huffman)Java实现代码简化版
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
這個網(wǎng)上發(fā)現(xiàn)的Huffuman編碼Java實(shí)現(xiàn)在組織上相對簡化,便于理解文件壓縮過程:提取文件統(tǒng)計(jì)字符頻度-根據(jù)字符頻度創(chuàng)建huffman樹-根據(jù)huffman樹生成huffman可變字長無前綴編碼-根據(jù)huffman編碼對文件中的字符轉(zhuǎn)化成二進(jìn)制串-將huffman編碼的二進(jìn)制串(非固定8位,可變字長)轉(zhuǎn)化成8位固定字節(jié)的字符并輸出文件。
代碼中對于Java數(shù)據(jù)類型的使用也值得參考。
package cn.hm;import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.PriorityQueue; import java.util.Set; public class HFMCondense { public class HFMNode { byte data; //存儲字節(jié)的數(shù)據(jù)域 int value; //字節(jié)出現(xiàn)的頻率 String code;//葉子結(jié)點(diǎn)的哈弗曼編碼 HFMNode lchild,rchild;//左右孩子的引用 //只指定數(shù)據(jù)的構(gòu)造體 public HFMNode(byte data,int rate){ this(data,rate,null,null); } //同時指定左右孩子的構(gòu)造體 public HFMNode(byte data,int value,HFMNode lchild,HFMNode rchild){ this.data=data; this.value=value; this.lchild=lchild; this.rchild=rchild; } } public static void main(String args[]){ String file="D://tmp//ori.txt"; HFMCondense condense=new HFMCondense(); HFMNode hfmTree=condense.HashMapToHFMTree(condense.readFiletoMap(file)); condense.HuffmanCoding(hfmTree, ""); System.out.println("開始壓縮..."); long start=System.currentTimeMillis(); condense.CompressFile(condense.createByteArray(condense.FileToString(file)),"D://tmp//des"); System.out.println("壓縮結(jié)束...用時:"+(System.currentTimeMillis()-start)); } /** * 讀取將要被壓縮的文件,統(tǒng)計(jì)每一個字符出現(xiàn)的頻率,并將得到的內(nèi)容存入HashMap中 * @param fileName 將要被壓縮的文件 * @return 每一個字節(jié)數(shù)出現(xiàn)的頻率所對應(yīng)的HashMap */ public HashMap<Byte,Integer> readFiletoMap(String fileName){ HashMap<Byte,Integer> hashMap=new HashMap<Byte,Integer>(); File file=new File(fileName); if(file.exists()){ try{ InputStream in=new FileInputStream(file); //創(chuàng)建與文件大小相同的字節(jié)數(shù)組 byte[] content=new byte[in.available()]; //讀取文件 in.read(content); //存入HashMap中 for(int i=0;i<content.length;i++){ //如果表中存在某一個鍵 if(hashMap.containsKey(content[i])){ //獲取該字節(jié)當(dāng)前的鍵值 int rate=hashMap.get(content[i]); //鍵值增大 rate++; hashMap.put(content[i], rate); } //如果不存在某一個字節(jié)對象,則將它存入HashMap當(dāng)中 else{ hashMap.put(content[i],1); } } in.close(); }catch(Exception e){ e.printStackTrace(); } } else{ System.out.println("文件不存在"); } return hashMap; } /** * 將HashMap中的元素取出來封裝到哈弗曼樹中,樹中葉子結(jié)點(diǎn)保存的是HashMap中的每一個鍵值與頻率 * @param map 讀取的Map * @return 哈夫曼樹的根結(jié)點(diǎn) */ public HFMNode HashMapToHFMTree(HashMap<Byte,Integer> map){ //得到存儲鍵值的系 Set<Byte> keys=map.keySet(); //得到迭代器對象 Iterator<Byte> iter=keys.iterator(); //如果還有值 while(iter.hasNext()){ byte key=iter.next();//獲取系中的鍵 int value=map.get(key);//得到該鍵出現(xiàn)的頻率 //創(chuàng)建結(jié)點(diǎn)并將結(jié)點(diǎn)對象加入到隊(duì)列當(dāng)中 HFMNode node=new HFMNode(key,value); nodeQueue.add(node); nodeList.add(node); } //當(dāng)所剩的結(jié)點(diǎn)數(shù)還大于兩個 while(nodeQueue.size()>=2){ //得到鍵值頻率最小的兩個結(jié)點(diǎn) HFMNode left=nodeQueue.poll(); HFMNode right=nodeQueue.poll(); //將這兩個結(jié)點(diǎn)組合起來生成新的結(jié)點(diǎn) HFMNode node=new HFMNode(left.data,left.value+right.value,left,right); nodeQueue.add(node); } //獲取隊(duì)列中的最后一個結(jié)點(diǎn)作為根結(jié)點(diǎn) HFMNode hfm=nodeQueue.poll(); return hfm; } /** * 為生成的哈弗曼樹進(jìn)行編碼,產(chǎn)生對應(yīng)的哈弗曼編碼表 * @param hfm 對應(yīng)的哈弗曼樹 * @param code 對應(yīng)生成的哈弗曼編碼 * @return 哈弗曼編碼表 */ //創(chuàng)建一個新的哈弗曼編碼表 HashMap<Byte,String> codeMap=new HashMap<Byte,String>(); public HashMap<Byte,String> HuffmanCoding(HFMNode hfm,String code){ //如果左子樹不為空,則左子樹編碼加1 if(hfm.lchild!=null){ HuffmanCoding(hfm.lchild,code+"1"); } //如果右子樹不為空,則右子樹編碼加0 if(hfm.rchild!=null){ HuffmanCoding(hfm.rchild,code+"0"); } //如果到達(dá)葉子結(jié)點(diǎn),則將元素放入HashMap中生成哈弗曼編碼表 if(hfm.lchild==null&&hfm.rchild==null){ codeMap.put(hfm.data,code); hfm.code=code; } return codeMap; } /** * 將哈弗曼編碼轉(zhuǎn)換成字符串 * @param fileName 讀取的文件名 * @return 編碼之后的哈弗曼字符串 */ public String CodeToString(String fileName){ File file=new File(fileName); String codeString=""; //如果文件存在 if(file.exists()){ try{ InputStream in=new FileInputStream(file); byte content[]=new byte[in.available()]; in.read(content); int i=0; int len=content.length;//得到文件的字節(jié)長度 int size=nodeList.size();//得到隊(duì)列的長度 while(i<len){ for(int j=0;j<size;j++){ if(content[i]==nodeList.get(j).data){ codeString+=nodeList.get(j).code; break; } } i++; } in.close(); }catch(Exception e){ e.printStackTrace(); } }else { System.out.println("文件不存在"); } return codeString; } /** * 將文件按照對應(yīng)的哈弗曼編碼表轉(zhuǎn)成01字符串 * @param fileName 讀入的文件名 * @return 轉(zhuǎn)譯后的字符串 */ public String FileToString(String fileName){ File file=new File(fileName); String StringContent=""; //如果文件存在 if(file.exists()){ try{ InputStream in=new FileInputStream(file); byte content[]=new byte[in.available()]; in.read(content); //循環(huán)轉(zhuǎn)譯 int len=content.length; for(int i=0;i<len;i++){ StringContent+=codeMap.get(content[i]); } in.close(); }catch(Exception e){ e.printStackTrace(); } }else{ System.out.println("文件不存在"); } return StringContent; } /** * 將轉(zhuǎn)譯后的01字符串重新轉(zhuǎn)換后放入新的字節(jié)數(shù)組當(dāng)中 * @param code 轉(zhuǎn)譯后的01字符串 * @return 新的字節(jié)數(shù)組,里面包含了壓縮后的字節(jié)內(nèi)容 */ public byte[] createByteArray(String code) { //將每8位字符分隔開來得到字節(jié)數(shù)組的長度 int size=code.length()/8; //截取得到字符串8整數(shù)后的最后幾個字符串 String destString=code.substring(size*8); byte dest[]=destString.getBytes(); //s用來記錄字節(jié)數(shù)組的單字節(jié)內(nèi)容 int s = 0; int i=0; int temp = 0; // 將字符數(shù)組轉(zhuǎn)換成字節(jié)數(shù)組,得到字符的字節(jié)內(nèi)容,方便將二進(jìn)制轉(zhuǎn)為十進(jìn)制 byte content[] = code.getBytes(); for (int k = 0; k < content.length; k++) { content[k] = (byte) (content[k] - 48); } //轉(zhuǎn)譯后的字節(jié)內(nèi)容數(shù)組 byte byteContent[]; if (content.length % 8 == 0) {// 如果該字符串正好是8的倍數(shù) byteContent = new byte[content.length / 8 + 1]; byteContent[byteContent.length - 1] = 0;// 那么返回的字節(jié)內(nèi)容數(shù)組的最后一個數(shù)就補(bǔ)0 } else { //否則該數(shù)組的最后一個數(shù)就是補(bǔ)0的個數(shù) byteContent = new byte[content.length / 8 + 2]; byteContent[byteContent.length - 1] = (byte) (8 - content.length % 8); } int bytelen=byteContent.length; int contentlen=content.length; // byteContent數(shù)組中最后一個是補(bǔ)0的個數(shù),實(shí)際操作到次后個元素 //Math.pow返回第一個參數(shù)的第二個參數(shù)次冪的值 while (i < bytelen - 2) { for (int j = 0; j < contentlen; j++) { if (content[j] == 1) {// 如果數(shù)組content的值為1 s =(int)(s + Math.pow(2, (7 - (j - 8 * i))));// 累積求和 }// if if ((j+1)%8==0) {// 當(dāng)有8個元素時 byteContent[i] = (byte) s;// 就將求出的和放入到byteContent數(shù)組中去 i++; s = 0;// 并重新使s的值賦為0 }// if }// for }// while int destlen=dest.length; for(int n=0;n<destlen;n++){ temp+=(dest[n]-48)*Math.pow(2, 7-n);//求倒數(shù)第2個字節(jié)的大小 } byteContent[byteContent.length - 2] = (byte) temp; return byteContent; } /** * 壓縮并輸出新文件 * @param content 壓縮后產(chǎn)生的新的字節(jié)數(shù)組 * @param fileName 輸出文件名 */ public void CompressFile(byte[] content,String fileName){ File file=null; //統(tǒng)一后綴名 if(!fileName.endsWith("hfm")){ file=new File(fileName+".hfm"); }else if(fileName.endsWith("hfm")){ file=new File(fileName); } int len=content.length; if(len>0){ try{ OutputStream out=new FileOutputStream(file); //將字節(jié)內(nèi)容寫入文件 out.write(content); out.close(); }catch(Exception e){ e.printStackTrace(); } }else{ System.out.println("壓縮出錯"); } } /** * 測試一下哈弗曼樹建立是否正確 * @param hfm */ public void PreOrderTraverse(HFMNode hfm){ if(hfm!=null){ System.out.print(hfm.value+" "); PreOrderTraverse(hfm.lchild); PreOrderTraverse(hfm.rchild); } } /** * 存儲哈弗曼樹結(jié)點(diǎn)的優(yōu)先隊(duì)列 */ ArrayList<HFMNode> nodeList=new ArrayList<HFMNode>(); PriorityQueue<HFMNode> nodeQueue=new PriorityQueue<HFMNode>(11,new MyComparator()); /** * 實(shí)例化的一個比較器類 */ class MyComparator implements Comparator<HFMNode>{ public int compare(HFMNode o1, HFMNode o2) { return o1.value-o2.value; } } }總結(jié)
以上是生活随笔為你收集整理的哈夫曼编码(Huffman)Java实现代码简化版的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 哈夫曼编码(Huffman)Java实现
- 下一篇: 最长公共子序列Java代码实现