青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

逛奔的蝸牛

我不聰明,但我會很努力

   ::  :: 新隨筆 ::  ::  :: 管理 ::

說到文件壓縮大家很容易想到的就是 rar,zip 等我們常見的壓縮格式。然而,還有一種就是大家在學習數據結構最常見到的哈夫曼樹的數據結構,以前還不知道他又什么用,其實他最大的用途就是用來做壓縮,也是一些 rar,zip 壓縮的祖先,稱為哈弗曼壓縮(什么你不知道誰是哈弗曼,也不知道哈弗曼壓縮,不急等下介紹)。

   隨著網絡與多媒體技術的興起,人們需要存儲和傳輸的數據越來越多,數據量越來越大,以前帶寬有限的傳輸網絡和容量有限的存儲介質難以滿足用戶的需求。

特別是聲音、圖像和視頻等媒體在人們的日常生活和工作中的地位日益突出,這個問題越發顯得嚴重和迫切。如今,數據壓縮技術早已是多媒體領域中的關鍵技術之一。

一、什么是哈弗曼壓縮

   Huffman( 哈夫曼 ) 算法在上世紀五十年代初提出來了,它是一種無損壓縮方法,在壓縮過程中不會丟失信息熵,而且可以證明Huffman 算法在無損壓縮算法中是最優的。 Huffman 原理簡單,實現起來也不困難,在現在的主流壓縮軟件得到了廣泛的應用。對應用程序、重要資料等絕對不允許信息丟失的壓縮場合, Huffman 算法是非常好的選擇。

二、怎么實現哈弗曼壓縮

   哈夫曼壓縮是個無損的壓縮算法,一般用來壓縮文本和程序文件。哈夫曼壓縮屬于可變代碼長度算法一族。意思是個體符號(例如,文本文件中的字符)用一個特定長度的位序列替代。因此,在文件中出現頻率高的符號,使用短的位序列,而那些很少出現的符號,則用較長的位序列。

   故我們得了解幾個概念:

  1 二叉樹 : 在計算機科學中,二叉樹是每個結點最多有兩個子樹的有序樹。通常子樹的根被稱作  左子樹   left subtree )和 右子樹   right subtree )。


 

2 哈夫曼編碼 (Huffman Coding) :是一種編碼方式,哈夫曼編碼是可變字長編碼 (VLC) 的一種。 uffman  1952 年提出一種編碼方法,該方法完全依據字符出現概率來構造異字頭的平均長 度最短的碼字,有時稱之為最佳編碼,一般就叫作 Huffman 編碼。

 

三、哈夫曼編碼生成步驟:

 掃描要壓縮的文件,對字符出現的頻率進行計算。

 把字符按出現的頻率進行排序,組成一個隊列。

 把出現頻率最低(權值)的兩個字符作為葉子節點,它們的權值之和為根節點組成一棵樹。

 把上面葉子節點的兩個字符從隊列中移除,并把它們組成的根節點加入到隊列。

 把隊列重新進行排序。重復步驟 ③④⑤ 直到隊列中只有一個節點為止。

 把這棵樹上的根節點定義為 0 (可自行定義 0  1 )左邊為 0 ,右邊為 1 。這樣就可以得到每個葉子節點的哈夫曼編碼了。


既如 (a)  (b)  (c)  (d) 幾個圖,就可以將離散型的數據轉化為樹型的了。

 

如果假設樹的左邊用0 表示右邊用 1 表示,則每一個數可以用一個 01 串表示出來。


則可以得到對應的編碼如下:

1-->110

2-->111

3-->10

4-->0

每一個01 串,既為每一個數字的哈弗曼編碼。

為什么能壓縮:

壓縮的時候當我們遇到了文本中的1  2  3  4 幾個字符的時候,我們不用原來的存儲,而是轉化為用它們的 01 串來存儲不久是能減小了空間占用了嗎。(什么 01 串不是比原來的字符還多了嗎?怎么減少?)大家應該知道的,計算機中我們存儲一個 int 型數據的時候一般式占用了 2^32-1  01 位,因為計算機中所有的數據都是最后轉化為二進制位去存儲的。所以,想想我們的編碼不就是只含有 0  1 嘛,因此我們就直接將編碼按照計算機的存儲規則用位的方法寫入進去就能實現壓縮了。

比如:

1這個數字,用整數寫進計算機硬盤去存儲,占用了 2^32-1 個二進制位

  而如果用它的哈弗曼編碼去存儲,只有110 三個二進制位。

效果顯而易見。

 

 

開始寫程序:

 

開始些程序之前,必須想好自己的文件存儲格式,和存儲規則是什么

為了簡便,我自定義存儲的基本信息,格式如下:

 

 

SaveCode[i].n    int型   // 每一個字節的編碼長度  i:0~256

B yte[]   byte數組型 // 每一個字節的編碼 SaveCode[i].node  String  01 串轉化而來。

 Byte[]  byte數組型 // 對文件中每一個 byte 的重新編碼的哈夫曼編碼寫入。

 

有了定義好的格式我們的讀寫就非常的方便了,

創建步驟:

①讀入文件中的所有字節:

Java代碼 
  1. // 創建文件輸入流  
  2.             java.io.FileInputStream fis = new java.io.FileInputStream(path);  
  3.             //讀入所有的文件字節  
  4.             while(fis.available()>0){  
  5.                 int i=fis.read();  
  6.                 byteCount[i]++;  
  7.             }  
 

②構建哈夫曼樹:

Java代碼 
  1. /** 
  2.     * 使用優先隊列構建哈弗曼樹 
  3.     */  
  4.    public void createTree(){  
  5.    //優先隊列  
  6.   PriorityQueue<hfmNode> nodeQueue = new PriorityQueue<hfmNode>();  
  7.     
  8.    //把所有的節點都加入到 隊列里面去  
  9.     for (int i=0;i<256;i++){  
  10.         if(byteCount[i]!=0){  
  11.             hfmNode node = new hfmNode(i,byteCount[i]);  
  12.             nodeQueue.add(node);//加入節點  
  13.         }  
  14.     }  
  15.     //構建哈弗曼樹  
  16.     while(nodeQueue.size()>1)  
  17.        {  
  18.         hfmNode min1 = nodeQueue.poll();//獲取隊列頭  
  19.         hfmNode min2 = nodeQueue.poll();  
  20.           
  21.         hfmNode result = new hfmNode(0,min1.times+min2.times);  
  22.         result.lChild=min1;  
  23.         result.rChild=min2;  
  24.            nodeQueue.add(result);//加入合并節點  
  25.        }  
  26.     root=nodeQueue.peek(); //得到根節點  
  27. }  
 

這里我采用了優先隊列的方法,因為優先隊列比較符合哈夫曼的構造方法,形式上也非常的相似。

③取得每一個葉子節點的哈夫曼編碼:

Java代碼 
  1. /** 
  2.    * 獲得葉子節點的哈弗曼編碼  
  3.    * @param root 根節點 
  4.    * @param s 
  5.    */  
  6.   public void getMB(hfmNode root,String s){  
  7.       if ((root.lChild==null)&&(root.rChild==null)){  
  8.         Code hc=new Code();  
  9.         hc.node=s;  
  10.         hc.n=s.length();  
  11.         System.out.println("節點"+root.data+"編碼"+hc.node);  
  12.         SaveCode[root.data]=hc;//保存字節  root.data 的編碼 HC  
  13.        }  
  14.     if (root.lChild!=null){//左0 右 1  
  15.         getMB(root.lChild,s+'0');  
  16.     }  
  17.     if (root.rChild!=null){  
  18.         getMB(root.rChild,s+'1');  
  19.         }  
  20.     }  
 

如此一來我們的哈夫曼樹就建好了。

下面就是按照我們之前定義的文件存儲格式直接寫進文件就可以了。

壓縮文件的寫入:

①  寫出0~256 每一個字節對應編碼的長度

Java代碼 
  1. //寫出每一個編碼的長度  
  2.           for (int i=0;i<256;i++){  
  3.             fos.write(SaveCode[i].n);  
  4.           }    
 

② 寫出每一個字節所對應的編碼

這一步較為復雜,因為JAVA 中沒有自己定義的二進制為寫入,我們就不得不將每 8  01 String 轉化為一個byte 再將 byte 寫入。但是,問題又來了不是所有的 01String 都是 8 的整數倍,所以就又得在不是 8 整數倍的String 后面補上 0

Java代碼 
  1. /////寫入每一個字節所對應的 String編碼  
  2.         int count=0;//記錄中轉的字符個數  
  3.         int i=0;//第i個字節  
  4.         String writes ="";  
  5.         String tranString="";//中轉字符串  
  6.         String waiteString;//保存所轉化的所有字符串  
  7.         while((i<256)||(count>=8)){  
  8.             //如果緩沖區的等待寫入字符大于8  
  9.             if (count>=8){  
  10.                 waiteString="";//清空要轉化的的碼  
  11.                 for (int t=0;t<8;t++){  
  12.                     waiteString=waiteString+writes.charAt(t);     
  13.                  }  
  14.               
  15.                 //將writes前八位刪掉  
  16.                 if (writes.length()>8){  
  17.                   tranString="";  
  18.                   for (int t=8;t<writes.length();t++){  
  19.                         tranString=tranString+writes.charAt(t);  
  20.                      }  
  21.                   writes="";  
  22.                   writes=tranString;  
  23.                   }else{  
  24.                       writes="";  
  25.                   }  
  26.                   
  27.                   count=count-8;//寫出一個8位的字節  
  28.                   int intw=changeString(waiteString);//得到String轉化為int  
  29.                   //寫入文件  
  30.                   fos.write(intw);  
  31.             }else{  
  32.                 //得到地i個字節的編碼信息,等待寫入  
  33.                 count=count+SaveCode[i].n;  
  34.                 writes=writes+SaveCode[i].node;  
  35.                 i++;  
  36.             }  
  37.         }  
  38.         //把所有編碼沒有足夠8的整數倍的String補0使得足夠8的整數倍,再寫入  
  39.         if (count>0){  
  40.             waiteString="";//清空要轉化的的碼  
  41.             for (int t=0;t<8;t++){  
  42.                 if (t<writes.length()){  
  43.                     waiteString=waiteString+writes.charAt(t);     
  44.                 }else{  
  45.                     waiteString=waiteString+'0';  
  46.                 }  
  47.             }  
  48.             fos.write(changeString(waiteString));//寫入  
  49.             System.out.println("寫入了->"+waiteString);  
  50.         }  
  51.   
  52.   
  53.    /** 
  54.     * 將一個八位的字符串轉成一個整數 
  55.     * @param s 
  56.     * @return 
  57.     */  
  58.    public int changeString(String s){  
  59.     return ((int)s.charAt(0)-48)*128+((int)s.charAt(1)-48)*64+((int)s.charAt(2)-48)*32  
  60.             +((int)s.charAt(3)-48)*16+((int)s.charAt(4)-48)*8+((int)s.charAt(5)-48)*4  
  61.             +((int)s.charAt(6)-48)*2+((int)s.charAt(7)-48);  
  62. }  
 

③ 將源文件中的所有byte 轉化為 01 哈夫曼編碼,寫入壓縮文件

   這一步也比較復雜,原理同上一步,在SaveCode 中查找一個 byte 所對應的哈夫曼編碼,不夠 8 的整數倍的就不足,再寫入。

   值得注意的事,最后一定要寫一個byte 表示,補了多少個 0 方便解壓縮時的刪除 0

 

Java代碼 
  1. //再次讀入文件的信息,對應每一個字節寫入編碼  
  2.             count=0;  
  3.             writes ="";  
  4.             tranString="";  
  5.             int idata=fis.read();  
  6.             //文件沒有讀完的時候  
  7.             while ((fis.available()>0)||(count>=8)){  
  8.                 //如果緩沖區的等待寫入字符大于8  
  9.                 if (count>=8){  
  10.                     waiteString="";//清空要轉化的的碼  
  11.                     for (int t=0;t<8;t++){  
  12.                         waiteString=waiteString+writes.charAt(t);     
  13.                      }  
  14.                      
  15.                    //將writes前八位刪掉  
  16.                    if (writes.length()>8){  
  17.                      tranString="";  
  18.                      for (int t=8;t<writes.length();t++){  
  19.                         tranString=tranString+writes.charAt(t);  
  20.                        }  
  21.                       writes="";  
  22.                      writes=tranString;  
  23.                     }else{  
  24.                       writes="";  
  25.                     }  
  26.                      
  27.                      
  28.                   count=count-8;//寫出一個8位的字節  
  29.                   int intw=changeString(waiteString);  
  30.                   fos.write(intw);//寫到文件中區  
  31.                 }else{  
  32.                     //讀入idata字節,對應編碼寫出信息  
  33.                     count=count+SaveCode[idata].n;  
  34.                     writes=writes+SaveCode[idata].node;  
  35.                     idata=fis.read();  
  36.                 }  
  37.             }  
  38.             count=count+SaveCode[idata].n;  
  39.             writes=writes+SaveCode[idata].node;  
  40.             //把count剩下的寫入  
  41.              int endsint=0;  
  42.              if (count>0){  
  43.                 waiteString="";//清空要轉化的的碼  
  44.                 for (int t=0;t<8;t++){  
  45.                     if (t<writes.length()){  
  46.                     waiteString=waiteString+writes.charAt(t);     
  47.                 }else{  
  48.                     waiteString=waiteString+'0';  
  49.                     endsint++;  
  50.                     }  
  51.                 }  
  52.                 fos.write(changeString(waiteString));//寫入所補的0  
 

如此一來,整個的壓縮過程就完畢了。

 

解壓縮過程:

    與壓縮過程剛好是反著來進行的,知道的存儲的方式我們就反著來讀取看看。

①  讀入所有字節所對應的編碼長度:

 

Java代碼 
  1. //讀入文件中的每一個編碼的長度  
  2.        for (int i=0;i<256;i++){  
  3.                Code hC=new Code();  
  4.                hC.n=fis.read();  
  5.                hC.node="";  
  6.            SaveCode[i]=hC;  
  7.        }  
 

 讀入每一個字節所對應的編碼

Java代碼 
  1. int i=0;  
  2.        int count=0;  
  3.        String coms="";  
  4.        //讀SaveCode中0~256字節的的數據  
  5.        while (i<256){  
  6.                //當緩存區的編碼長度比編碼[i]的長度長的時候  
  7.                if (coms.length()>=SaveCode[i].n){  
  8.                       for (int t=0;t<SaveCode[i].n;t++){//SaveCode[i].node取得該編碼  
  9.                           SaveCode[i].node=SaveCode[i].node+coms.charAt(t);  
  10.                       }  
  11.                         
  12.                       //把coms前這幾位去掉  
  13.                       String coms2="";  
  14.                       for (int t=SaveCode[i].n;t<coms.length();t++){  
  15.                           coms2=coms2+coms.charAt(t);  
  16.                       }  
  17.                       coms="";  
  18.                       coms=coms2;//累計的下一個編碼,繼續的使用  
  19.                       i++;  
  20.                }else{  
  21.                     //如果已經沒有寫出的話,再度入一個byte轉化為01 String  
  22.                     coms=coms+IntToString(fis.read());  
  23.                }  
  24.        }  
 

 讀入文件的哈夫曼編碼

得注意的事,前面我們又補0 的位,所以在我們讀取的時候,記得把之前所補得東西,給刪除掉就 OK 了。

Java代碼 
  1. String rsrg;//存轉換成的Sting  
  2.        String compString="";//存要比較的字符串  
  3.        //讀入文件,寫出文件  
  4.        while(fis.available()>1){  
  5.            if (search(compString)>=0){  
  6.                int cint=search(compString);  
  7.                fos.write(cint);  
  8.                //刪掉前n個數據  
  9.                String compString2="";  
  10.                for (int t=SaveCode[cint].n;t<compString.length();t++){  
  11.                   compString2=compString2+compString.charAt(t);  
  12.                }  
  13.                compString="";  
  14.                compString=compString2;  
  15.                  
  16.            }else{//繼續讀入  
  17.                compString=compString+IntToString(fis.read());  
  18.            }  
  19.        }  
  20.          
  21.          
  22.        //處理最后一個字節  
  23.       int cint=fis.read();  
  24.       String compString2="";  
  25.       for (int t=0;t<compString.length()-cint;t++){  
  26.          compString2=compString2+compString.charAt(t);  
  27.       }  
  28.       compString=compString2;  
  29.         
  30.         
  31.        //刪掉前n個數據  
  32.       while (compString.length()>0){  
  33.            int ccint=search(compString);  
  34.            fos.write(ccint);     
  35.            compString2="";  
  36.            for (int t=SaveCode[ccint].n;t<compString.length();t++){  
  37.             compString2=compString2+compString.charAt(t);  
  38.            }  
  39.            compString="";  
  40.            compString=compString2;  
  41.       }  
 
如此而來,一個簡單的小壓縮軟件就實現了。

本次的文件讀寫比較復雜,之前些的時候遇到了很多的困難,參考了很多前輩的思路和代碼,今天終于完成了,我~內牛滿面
因為沒有對其做任何的優化壓縮的效率比較低,時間比較長,個位大俠不要見笑。

From: http://stchou.javaeye.com/blog/833232
posted on 2010-12-04 18:27 逛奔的蝸牛 閱讀(3990) 評論(1)  編輯 收藏 引用 所屬分類: Java

評論

# re: Java:自己動手寫壓縮軟件,超詳細解釋(哈夫曼實現)[未登錄] 2011-08-13 10:35 123
很多方法都木有寫出來,看的暈死了···很桑心···  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            制服丝袜激情欧洲亚洲| 久久久久一区二区| 亚洲免费一级电影| 欧美高清在线一区二区| 亚洲视频在线一区| 亚洲视频狠狠| 久久久久久香蕉网| 亚洲香蕉成视频在线观看| 欧美国产日韩一区| 在线看成人片| 欧美a一区二区| 久久久久久午夜| 今天的高清视频免费播放成人 | 亚洲欧洲另类国产综合| 女生裸体视频一区二区三区| 欧美一区激情| 国产在线精品一区二区中文| 久久久久久久97| 久久精品女人| 伊人久久成人| 亚洲国产精品第一区二区三区| 久久精品国产99精品国产亚洲性色| 国产欧美日韩在线视频| 欧美一区二区三区在线免费观看| 亚洲欧美成人在线| 国产私拍一区| 欧美不卡福利| 欧美精品在线观看播放| 中日韩美女免费视频网址在线观看 | 欧美福利视频网站| 欧美jizzhd精品欧美巨大免费| 最近中文字幕mv在线一区二区三区四区 | 国产亚洲视频在线| 欧美综合国产精品久久丁香| 亚洲欧美日韩视频二区| 狠狠色综合色区| 亚洲高清激情| 国产精品久久久久久久9999 | 欧美成人精品一区二区三区| 你懂的国产精品| 亚洲一区二区三区视频播放| 午夜日韩视频| 亚洲国产精品欧美一二99| 91久久香蕉国产日韩欧美9色| 欧美日在线观看| 久久裸体视频| 欧美日韩国产一区二区| 久久av一区二区三区亚洲| 免费欧美在线| 性色av一区二区三区在线观看| 久久久7777| 亚洲午夜av| 久久久久久久久久久久久女国产乱| 亚洲精品黄色| 欧美在线视频免费观看| 9久re热视频在线精品| 午夜免费久久久久| 久久精品日产第一区二区三区| 国产精品久久久久久久久久免费| 久久精品一二三| 欧美日韩第一区| 老**午夜毛片一区二区三区| 国产精品扒开腿做爽爽爽视频| 欧美成人久久| 国产性色一区二区| 在线视频精品一区| 亚洲欧洲一级| 久久国产乱子精品免费女 | 欧美一区二区三区久久精品茉莉花| 久久精品日产第一区二区| 亚洲无线视频| 欧美第一黄网免费网站| 麻豆国产精品一区二区三区| 久久精品欧美日韩| 欧美国产一区二区在线观看| 久久久999精品| 国产精品麻豆欧美日韩ww | 久久综合久久综合这里只有精品| 亚洲影视在线| 欧美激情欧美狂野欧美精品| 久久人体大胆视频| 国产欧美一区二区在线观看| 9人人澡人人爽人人精品| 亚洲激情国产精品| 久久蜜臀精品av| 久久综合给合久久狠狠色| 国产精品久久久久久久免费软件 | 欧美日韩三区四区| 亚洲激情一区| 亚洲理论电影网| 免费日韩av片| 欧美成人免费播放| 亚洲国产91精品在线观看| 午夜亚洲性色福利视频| 久久精品国产一区二区三| 国产精品亚洲不卡a| 亚洲综合精品一区二区| 午夜在线精品偷拍| 国产精品女主播在线观看| 一区二区欧美日韩| 午夜国产精品视频| 国产精品综合视频| 欧美在线一区二区三区| 欧美呦呦网站| 黄色精品在线看| 老司机67194精品线观看| 久久在线播放| 亚洲国产综合视频在线观看| 欧美成人亚洲成人| 亚洲免费观看高清在线观看 | 免费在线欧美黄色| 国产精品网红福利| 欧美一级片在线播放| 久久精品成人欧美大片古装| 国产一区二区三区免费观看| 久久经典综合| 亚洲高清精品中出| 亚洲一区二区精品视频| 国产精品影视天天线| 久久久久九九九| 国产精品欧美日韩一区二区| 亚洲线精品一区二区三区八戒| 久久精品123| 亚洲国产天堂网精品网站| 欧美激情精品久久久久久蜜臀| 日韩一区二区精品在线观看| 午夜精品美女自拍福到在线| 国产亚洲一区在线播放| 欧美a级大片| 中文亚洲视频在线| 牛夜精品久久久久久久99黑人| av成人免费| 国内欧美视频一区二区| 欧美激情欧美狂野欧美精品| 亚洲中字在线| 亚洲国产精品一区二区第四页av | 久久久久99精品国产片| 亚洲国产精品黑人久久久 | 亚洲视频免费观看| 精品999成人| 国产精品久久9| 麻豆国产精品一区二区三区| 亚洲视频视频在线| 亚洲第一二三四五区| 亚洲欧美激情一区| 亚洲精品麻豆| 国内综合精品午夜久久资源| 欧美日韩亚洲不卡| 欧美成年网站| 久久国产日本精品| 一本色道久久综合精品竹菊| 蜜臀av一级做a爰片久久| 午夜欧美不卡精品aaaaa| 亚洲日本欧美| 一区二区在线视频播放| 国产农村妇女精品一二区| 欧美日韩国产影片| 欧美电影在线| 老司机精品福利视频| 欧美专区在线| 亚洲欧美日韩系列| 亚洲一区日韩| 亚洲视频欧美在线| 日韩一二在线观看| 亚洲精品久久久久久一区二区 | 久久久999国产| 亚洲欧美怡红院| 亚洲伊人网站| 亚洲视频一区在线观看| 亚洲精品视频免费观看| 亚洲国产精品久久久久婷婷老年 | 国一区二区在线观看| 国产精品久久久久9999吃药| 欧美日韩的一区二区| 欧美精品一区在线播放| 亚洲影院污污.| 99成人精品| 亚洲精品日韩在线观看| 亚洲欧洲在线一区| 亚洲激情综合| 亚洲欧洲一区二区三区久久| 亚洲国产精品综合| 亚洲啪啪91| 一区二区91| 在线亚洲成人| 亚洲曰本av电影| 欧美一级片在线播放| 欧美在线www| 久久久久久噜噜噜久久久精品| 久久久噜噜噜久久人人看| 麻豆精品网站| 欧美日韩的一区二区| 国产精品久久久久7777婷婷| 国产精品福利网站| 国产午夜久久| 亚洲电影有码| 中文国产一区| 久久国产婷婷国产香蕉| 蜜桃av综合| 亚洲美洲欧洲综合国产一区|