1. 程式人生 > 其它 >有趣的演算法(三)——Hash演算法

有趣的演算法(三)——Hash演算法

有趣的演算法(三)——Hash演算法

(原創內容,轉載請註明來源,謝謝)

一、Hash演算法

近期看到用hash實現基於hash的簡單的小型資料庫(傳統大型資料庫用的都是B+tree),感覺挺感興趣,故先研究hash演算法,近期會用hash實現一個小的資料庫。

Hash表(Hash Table)又稱為散列表,通過把關鍵字key對映到陣列的一個位置,來訪問記錄。這個對映函式稱為hash函式,存放記錄的陣列稱為hash表。

1、hash函式

作用是把任意長度的輸入,通過hash演算法得到固定函式的輸出,輸出的內容就是hash值。這種對映是一種壓縮對映,即輸出的內容佔用的儲存空間可能會小於輸入的內容。因此不同的key經過hash可能會得到同一個結果。

好的hash使每個關鍵字均勻分佈到hash表的任一個位置,並於其他已被雜湊到hash表中的關鍵字不衝突。

根據關鍵字的不同,可能設計不同的hash演算法。

2、直接取餘法——適用整數

用關鍵字k除以hash表的大小m取餘,得到的結果即為結果。

h(k) = k mod m。例如雜湊表大小20,關鍵字是30,則h(20) = 10。

3、乘積取整法——適用小數

使用關鍵字k乘以一個常數A(0<A<1),取出kA的小數部分,乘以hash表的大小,向下取整即可。

hash(k)= floor(m*(kA mod 1)),kA mod 1表示kA的小數部分,floor是取整操作。

4、ASCII碼處理法——適用字串

字串無法進行取餘或者取整,則可以使用把每個字元取整求和,再按照上面的方法對結果進行處理。

5、經典hash演算法:DJB hashfunction(俗稱Times33)

該演算法效率和隨機性很高,運用廣泛,包括Perl、Berkeley DB、Apache、MFC、STL、PHP等的Hash,都用的此演算法。

該演算法的核心是將每一位都乘以33,再加上原來的值。

程式碼實現方法(C語言)如下:

         unsignedlong time33(char const *str, int len)
    {
       unsigned long  hash = 0;
       for (int i = 0; i < len; i++) {
           hash = ((hash <<5) + hash) + (unsigned long) str[i];
       }
       return hash;
}

其中,採用<<方式,即將值乘以2的5次方(32)。

二、Hash表

1、演算法

hash表的時間複雜的O(1),即key通過hash函式,找到值所在的地方。要構建hash表必須建立一個足夠大的陣列用於存放資料,另外還需要一個hash函式把關鍵字key對映到陣列的某個位置。具體實現步驟如下:

1)建立一個固定大小的陣列用於存放資料。

2)設計hash函式。

3)通過hash函式把關鍵字對映到陣列的某個位置,並在此位置上進行資料存取。

2、用PHP實現hash表

1)定義hashtable類

<?php
classHashTable{
private $buckets;
private $size = 10;
public function __construct($size = 0){
if($size > 0){
$this->size =$size;
}
$this->buckets = new SplFixedArray($this->size);
}
}

hash表採用定長來儲存資料,其中buckets是一個數組,其key是hash函式的結果,值用於存放原值。buckets的陣列不採用array,而採用php的SPL中的SplFixedArray,該類要求初始化的時候需要一個定長,並且陣列的key只能是整數。這個陣列更接近原生的c語言,效率更高。

2)定義hash函式(Times33,字串取ascii,結果和10)

private functionhashFunction($string){
         $len = strlen($string);
         $hash = 0;
         for($i=0;$i<$len;$i++){
         $hash += $hash<<5+ ord($key[$i]);
         $hash %=$this->size;
}
return $hash;
}

3)增刪改查

         //新增,相同則覆蓋
         publicfunction insert($key, $val){
                   $key= $this->hashFunction($key);
                   $this->buckets[$key]= $val;
                   return$this->buckets;
         }
         //查詢
         publicfunction search($key){
                   $key= $this->hashFunction($key);
                   return$this->buckets[$key];
         }
         //刪除
         publicfunction delete($key){
                   $key= $this->hashFunction($key);
                   if(isset($this->buckets[$key])){
                            unset($this->buckets[$key]); 
                   }
         }
         //修改,不存在則新增
         publicfunction update($key, $val){
                   $key= $this->hashFunction($key);
                   $this->buckets[$key]= $val;
                   return$this->buckets;
         }

3、Hash表衝突問題

因為key是通過一定的計算得到的結果,且通常傳入的key和計算的key不是一一對應的,因此可能會存在衝突。要解決衝突,可以使用拉鍊法,即將所有相同的值放在一個連結串列中。此時,除了需儲存value,還要儲存key和下一個value的位置。

因此,需要引入一個新的類:

         classHashNode{
         public $key;
         public $value;
         public $next;
         public function __construct($key,$value, HashNode $next = null){
         $this->key = $key;
         $this->value=$value;
         $this->next =$next;
}
}

此時,相應的操作變化如下:

         //新增,連結串列方式,重複則新增到相應的連結串列
         publicfunction insert($key, $val){
                   $key= $this->hashFunction($key);
                   if(isset($this->buckets[$key])){
                            $node= new HashNode($key, $val, $this->buckets[$key]);
                   }else{
                            $node = new HashNode($key, $val);
                   }
                   unset($this->buckets[$key);
                   $this->buckets[$key]= $node;
                   return$this->buckets;
         }
         //查詢
         publicfunction search($key){
                   $key= $this->hashFunction($key);
                   if(!isset($this->buckets[$key])){
                            returnnull;
                   }
                   $current= $this->buckets[$key];
                   while(isset($current)){
                            if($current->key== $key){
                                     return$current->value;
                            }
                            $current= $current->next;
                   }                                   
                   returnnull;
         }
         //修改,不存在則返回false
         publicfunction update($key, $val){
                   $key= $this->hashFunction($key);
                   if(!isset($this->buckets[$key])){
                            returnfalse;
                   }
                   $current= $this->buckets[$key];
                   $isSuccess= false;
                   while(isset($current)){
                            if($current->key== $key){
                                     $current->value= $val;
                                     $isSuccess= true;
                                     break;
                            }
                            $current= $current->next;                       
                   }
                   return$isSuccess;
         }

——written by linhxx 2017.08.18