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

逛奔的蝸牛

我不聰明,但我會很努力

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

說到文件壓縮大家很容易想到的就是 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>
            欧美激情欧美激情在线五月| 日韩天堂在线视频| 亚洲美女在线国产| 久久在线播放| 久久精品免视看| 国产精品99久久久久久有的能看| 欧美美女日韩| 亚洲综合精品一区二区| 亚洲一区欧美二区| 欧美亚洲专区| 久久综合给合| 欧美—级高清免费播放| 欧美日韩福利在线观看| 欧美人在线视频| 欧美三级网址| 国产精品色午夜在线观看| 欧美午夜精品久久久久免费视| 欧美久久婷婷综合色| 欧美理论在线播放| 国产精品私拍pans大尺度在线 | 欧美黄色aa电影| 欧美~级网站不卡| 亚洲精品三级| 亚洲图片欧洲图片av| 国产精品乱码一区二区三区| 欧美日韩你懂的| 欧美性做爰毛片| 国产亚洲精品aa午夜观看| 黄色欧美成人| 99国产精品久久| 亚洲欧美国产日韩中文字幕| 久久成人免费网| 亚洲人成亚洲人成在线观看| 最新成人在线| 午夜精品久久一牛影视| 欧美成人在线网站| 国产精品久久久久久久第一福利| 一区二区视频免费在线观看| aa国产精品| 午夜在线观看免费一区| 美女精品在线| 亚洲一级黄色片| 免费美女久久99| 国产日韩在线一区| 一本综合久久| 欧美劲爆第一页| 国产主播一区二区三区四区| 99re视频这里只有精品| 久久久久久久久久看片| 亚洲视频在线看| 欧美激情综合色| 精品电影在线观看| 欧美综合国产| 在线亚洲国产精品网站| 欧美高清在线视频| 亚洲第一在线综合网站| 久久国产黑丝| 亚洲欧美日韩在线不卡| 欧美精品一区二区在线播放| 亚洲激情视频在线| 奶水喷射视频一区| 亚洲欧美视频在线| 欧美日韩1区2区| 99热精品在线| 亚洲免费精彩视频| 欧美视频第二页| 亚洲综合激情| 亚洲视频中文字幕| 国产精品日本| 欧美一级视频| 欧美一区二区| 国产一区二区三区日韩| 亚洲欧美日韩国产综合精品二区| 欧美性色综合| 久久中文字幕一区| 国产色综合久久| 久久精品主播| 欧美一区二视频在线免费观看| 国产精品v片在线观看不卡| 日韩视频一区二区在线观看| 欧美国产在线视频| 久久噜噜噜精品国产亚洲综合| 伊人成人在线视频| 免费不卡中文字幕视频| 欧美激情中文字幕一区二区| 亚洲人永久免费| 一本到12不卡视频在线dvd| 欧美日韩在线不卡一区| 一区二区三区久久精品| 99精品99久久久久久宅男| 国产精品久久久久77777| 亚洲欧美国产va在线影院| 欧美一二区视频| 亚洲黄色在线视频| 亚洲国产日韩欧美一区二区三区| 欧美成人免费网站| 亚洲图中文字幕| 欧美一二三区在线观看| 亚洲激情不卡| 中国av一区| 国产伦一区二区三区色一情| 久久久无码精品亚洲日韩按摩| 久久不射2019中文字幕| 激情欧美一区二区| 亚洲日本成人在线观看| 国产精品国产三级国产专区53| 亚洲欧美日韩天堂| 久久精品卡一| 亚洲香蕉伊综合在人在线视看| 亚洲欧美久久久| 亚洲国产日韩综合一区| 亚洲免费在线| 99在线热播精品免费99热| 久久精品91久久久久久再现| 99国产精品久久久久久久| 亚洲免费综合| 亚洲国产成人精品久久| 亚洲一区二区高清视频| 99在线精品视频| 久久精品国产免费观看| 99视频精品全国免费| 日韩一二三在线视频播| 国产一区二区你懂的| 亚洲国产精品久久久| 在线精品视频免费观看| 亚洲第一中文字幕在线观看| 欧美日韩网站| 久久国产一区| 你懂的视频欧美| 亚洲一区二区三区视频播放| 欧美成年网站| 亚洲国产美女| 亚洲视频一二| 91久久国产综合久久| 国产亚洲欧美日韩日本| 国产精品一区二区三区免费观看| 久久久噜噜噜久久狠狠50岁| 亚洲无线一线二线三线区别av| 亚洲美女性视频| 久久久夜色精品亚洲| 国产农村妇女精品| 亚洲免费一级电影| 久久亚洲欧美| 亚洲大胆人体视频| 一区二区三区免费网站| 影音先锋久久| 久久久久综合一区二区三区| 久久久国产精品一区| 亚洲精品中文字幕有码专区| 欧美一二三区精品| 亚洲一级二级在线| 欧美1区2区| 久久综合伊人77777蜜臀| 国产日韩欧美精品一区| 午夜精品视频| 欧美一区二区三区久久精品茉莉花| 欧美亚洲一区二区在线| 亚洲欧美美女| 欧美中文日韩| 嫩草伊人久久精品少妇av杨幂| 欧美午夜性色大片在线观看| 欧美v日韩v国产v| 国产一区二区三区直播精品电影| 亚洲电影免费观看高清完整版| 国产伦理一区| 久久久国产精彩视频美女艺术照福利| 欧美在线播放视频| 韩国一区二区在线观看| 久久精品国产69国产精品亚洲| 久久国产精品久久国产精品| 一区二区三区在线免费播放| 久久综合网hezyo| 91久久在线观看| 午夜天堂精品久久久久| 国产一区免费视频| 久久久久se| 亚洲激情在线视频| 欧美一区二区三区电影在线观看| 国产私拍一区| 久久久久欧美| 午夜精品一区二区三区在线播放| 亚洲一区久久久| 亚洲欧美区自拍先锋| 国产精品家教| 亚洲免费在线播放| 午夜日本精品| 久久久久久网址| 海角社区69精品视频| 欧美不卡三区| 亚洲天堂成人在线观看| 久久gogo国模裸体人体| 亚洲激情在线播放| 欧美二区乱c少妇| 亚洲欧美国产高清va在线播| 美日韩丰满少妇在线观看| 99精品欧美一区二区三区| 国产精品女人久久久久久| 中日韩美女免费视频网址在线观看| 午夜精品国产| 亚洲精品乱码久久久久久日本蜜臀|