數據結構:哈夫曼編碼(php版)

演示網址:http://huffman.sinaapp.com/php

源文件下載地址:http://xiaocao.u.qiniudn.com/work/huffman-2013-12-19.zipcss



概述下:

    哈夫曼樹─即最優二叉樹,帶權路徑長度最小的二叉樹,常常應用於數據壓縮。 在計算機信息處理中,「哈夫曼編碼」是一種一致性編碼法(又稱「熵編碼法」),用於數據的無損耗壓縮。     簡單的,就是靠權值排序,而後,轉碼,最優保存。html

實現功能:

  • 保存譯碼:在服務器端保存源字符串,命名爲:」Encording.txt」node

  • 保存編碼:在服務器端保存壓縮後壓縮碼,命名爲:」Decording.txt」算法

  • 保存哈夫曼樹:在服務器端保存哈夫曼樹數組,命名爲:」Huffman.txt」數組

  • 瀏覽器本地保存:在本地緩存輸入歷史。而且實現自行選擇本地版本。瀏覽器


開始實現

1、先看整個程序流程圖

2、接下來是哈夫曼流程圖

3、程序包含文件

前臺表單提交頁面,後臺表單處理頁面,以及哈夫曼壓縮,解壓縮系統。包含兩個主文件:huffman.php和index.php(另外還包含style.css控制樣式,huffman.js保存緩存和控制交互。)   
|--index.php(處理基本表單,數據保存) |--huffman.php(壓縮,解壓縮) |--style.css(控制樣式) |--huffman.js(保存緩存和控制交互)緩存

源碼:

huffman.php 
服務器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/*  先介紹幾個php內置函數:
     asort()函數對數組進行排序並保持索引關係。主要用於對那些單元順序很重要的結合數組進行排序。
     array_shift() 函數刪除數組中的第一個元素,並返回被刪除元素的值。
     str_pad(string, long, 補充元素, 類型) 函數把字符串填充爲指定的長度
     base_convert() 函數在任意進制之間轉換數
     substr(string, start, end) 方法可在字符串中抽取從 start 下標開始的指定數目的字符。
*/
 
/*=========================================================*/
/*=========================================================*/
/*=========================================================*/
 
/*
基於靜態huffman編碼的壓縮,要保存的文件有huffmantree(數組), binary(字符串)
本文以PHP做爲描述語言較詳細講解huffman樹原理及應用
*/
?>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
class huffman
{
     /*
      * 壓縮入口
      * $str:待壓縮的字符串
      */
     public function encode( $str )
     {      
         $len = strlen ( $str );
         //計算每一個字符權重值(出現的頻度)
         //ord(),是php內置ASCII轉化函數,將字符轉化成ASCII碼
         for ( $i =0; $i < $len ; $i ++) $array [ord( $str { $i })]=0; //初始化數組
         for ( $i =0; $i < $len ; $i ++)
             $array [ord( $str { $i })]++;
         $HuffmanArray = array ();
         //asort()函數對數組進行排序並保持索引關係。主要用於對那些單元順序很重要的結合數組進行排序。
         asort( $array );
         /**
         * 構造huffman樹,時間複雜度O(nlogn)
         * 選擇兩個使用頻率較小<字符在字符串中出現的次數>的結點合併生成出一個樹
         */
         //循環建立哈夫曼樹數組
         while ( $item1 = each( $array ))
         {      
             $item2 = each( $array );
             $this ->creat_tree( $item1 , $item2 , $array , $HuffmanArray );
             asort( $array );
         }
         //array_shift() 函數刪除數組中的第一個元素,並返回被刪除元素的值。
         $HuffmanArray = array_shift ( $HuffmanArray );
         $tab =null;
         $code_tab = $this ->creat_tab( $HuffmanArray , $tab );
         //壓縮&轉換整個字符串爲二進制表達式
         $binary =null;
         for ( $i =0; $i < $len ; $i ++)  $binary .= $tab [ord( $str { $i })];       
         //轉化爲壓縮後的字符串
         $code = $this ->encode_bin( $binary );
         //靜態huffman編碼算法壓縮後需保留huffman樹
         return array ( 'tree' => $HuffmanArray , 'len' => strlen ( $binary ), 'code' => $code );
     }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 解壓縮入口
* $huffman:解壓所使用的huffman樹
* $str:被壓縮的字符
* $blen:壓縮前的位長度
*/
public function decode( $huffman , $str , $blen )
{
     $len = strlen ( $str );
     $binary =null;
     //將編碼解爲二進制表達式
     for ( $i =0; $i < $len ; $i ++)      
     $binary .= str_pad ( base_convert (ord( $str { $i }),10,2),8, '0' ,STR_PAD_LEFT);
     $binary = substr ( $binary ,0, $blen );
     return $this ->decode_tree( $binary , $huffman , $huffman );
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 將壓縮後的二進制表達式再轉爲字符串
* $binary:二進制表達式字串
*/
private function encode_bin( $binary )
{
     $len = strlen ( $binary );
     //二進制轉字符須要整8位,不足8位補0
     $blen = $len +8- $len %8;
     $binary = str_pad ( $binary , $blen , '0' );
     $encode =null;
     //每8位轉爲一個字符
     for ( $i =7; $i < $blen ; $i +=8)
     {
         $frag = substr ( $binary , $i -7,8);
         //base_convert() 函數在任意進制之間轉換數字
         $encode .= chr ( base_convert ( $frag ,2,10));
     }
     return $encode ;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
  * 構造huffman樹,使用貪婪算法選擇最小的兩個元素做爲樹的子節點
  * $item1:權重最小的元素1
  * $item2:權重次小的元素2
  * $array:全部字符出現次數表<權重表>
  *$HuffmanArray:保存生成的huffman樹結構
*/
private function creat_tree( $item1 , $item2 ,& $array ,& $HuffmanArray )
{     
     list( $key , $weight )= $item1 ;
     list( $key2 , $weight2 )= $item2 ;
     //假設當前樹的左右節點爲空節點
     $c1 = $key ;
     $c2 = $key2 ;
     //判斷兩個元素若爲樹則直接做爲節點併入主樹
     if (isset( $HuffmanArray [ $key2 ]))
     {
         $c2 = $HuffmanArray [ $key2 ];       
         unset( $HuffmanArray [ $key2 ]);
     }
     if (isset( $HuffmanArray [ $key ]))
     {
         $c1 = $HuffmanArray [ $key ];
         unset( $HuffmanArray [ $key ]);
     }
     //設置樹結點權值
     $array [ $key2 ]= $weight + $weight2 ;                                                       
     //合併節點後刪除元素
     unset( $array [ $key ]);
     //合併到huffman樹中
     $HuffmanArray [ $key2 ]= array (0=> $c1 ,1=> $c2 );
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 廣度優先遍歷樹,獲得全部原字符對應的二進制表達式<01010...>
* $tree:已經構建好的huffman樹
* $tab:編碼表,保存全部字符對應的編碼
* $a0:左遍歷樹的路徑<11010...>
* $a1:右遍歷樹的路徑
*/
private function creat_tab( $tree ,& $tab , $a0 =null, $a1 =null)
{      
 
     if ( $tree ==null) return ;
     //遍歷左右子樹
 
     foreach ( $tree as $node => $ctree )
     {    
         if ( is_array ( $ctree ))
         {
             //判斷未到達葉子節點時再向下遍歷
             $this ->creat_tab( $ctree , $tab , $a0 . $node , $a1 . $node );
         } else {
             //遍歷到葉子節點<原字符ascii碼>時的全部路徑,既二進制表達式,下同
             $tab [ $ctree ]=${ 'a' . $node }. $node ;
         }
     }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
     /**
     * 使用進製表達式深度優先遍歷樹,0爲左子樹,1爲右子樹,而到根節點,即爲二進制表達式所指向的原字符
     * $binary:二進制表達式字串
     * $huffman:huffman樹
     * $tree:當前所遍歷的子樹
     * $i:指向二進制表達式字串的<指針>
     * $code:解碼後的字符串
     */
     private function decode_tree( $binary , $huffman , $tree , $i =0, $code =null)
     {      
         $lr = $binary { $i };
         //遍歷完成
         if ( $lr ==null) return $code ;
         //判斷是否到根節點,根節點既爲二進制表達式對應的原字符ascii碼
         if ( is_array ( $tree [ $lr ]))
         {
             //繼續向下遍歷子樹
             return $this ->decode_tree( $binary , $huffman , $tree [ $lr ], $i +1, $code );
         } else {
             //將二進制表達式解碼爲原字符
             $code .= chr ( $tree [ $lr ]);
             return $this ->decode_tree( $binary , $huffman , $huffman , $i +1, $code );
         }
     }
}
?>


下面是js緩存源碼app


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$( document ).ready( function (){
 
/*函數寫的比較亂,慢慢改進*/
 
//flag是用來判斷,是否須要添加緩存計數
var flag= true ;
 
function get_Storage(key){
     return window.localStorage.getItem(key);
}
 
function set_Storage(key, value){
     return window.localStorage.setItem(key, value);
}
 
/*初始化函數*/
function init(){
     var node = new huffman();
     $( "#submited" ).click( function (event){
         var value = $( "#node" ).val();
         if (value!= '' ){
             node.set($( "#node" ).val());
         }
     });
     //顯示選取的文件,添加到div中
     $( '#local' ).click( function (event){
         var len= get_Storage( "length" );
         var text= "" ;
         for ( var i = 1; i <= len; i++){
             text += '<button type="button" class="item btn btn-primary" data=' +i+ '>' +get_Storage(i)+ '</button><br><br>' ;
         }
         //若是有內容就顯示,不然,提示用戶添加
         if (len){
             $( '#modal-body' ).html(text);
         }
         //選擇本地緩存設置
         $( '.item' ).click( function (event){
             var id = $( this ).attr( "data" );
             $( "#node" ).val(get_Storage(id));
             //設置flag標誌
             flag= false ;
             $( "#close" ).click();
             $( "#submited" ).click();
         });
     });
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*構建原型*/
function huffman(){}
huffman.prototype={
     set: function (value){
         if (flag){
             if (get_Storage( "length" )){
                 var num = parseInt( get_Storage( "length" ))+1;
                 set_Storage( "length" , num);
             } else {
                 set_Storage( "length" ,1);
             }
             set_Storage(get_Storage( "length" ),value);
         }
     },
     get: function (){
         var i = get_Storage( "length" );
         var array = new Array();
         for (p=0; p<i; p++){
             var item=get_Storage(p);
             array.push(item);
         }
         this .show(array);
     },
     show: function (array){
         //這裏要顯示對話框
     }
}   
 
window.onload=init();
});
相關文章
相關標籤/搜索