區域鏈準備篇:散列介紹(一)

散列

 散列表(hash table)的實現叫作散列(hashing)。
 散列是一種用於以常數平均時間執行插入、刪除和查找的技術。

1. 散列基本想法
理想的散列表,數據結構只不過是一個包含一些項(item)的,具體固定大小的數組。
通常查找是對項的某個部分(數據域)進行,這部分叫關鍵字(key)。
散列表通常從0到TableSize-1變化,每個關鍵字被映射到0到TableSize-1這個範圍中的某個數,並且被放到適當的單元中。這個映射過程叫散列函數(hash function)
通過一個散列函數,將關鍵字John散列到3….mary散列到7。 這裏寫圖片描述
關鍵問題:選擇一個函數,決定兩個關鍵字散列到同一個值的時候(衝突collision),應該做什麼以及如何確定散列表的大小。

2. 散列函數
如果關鍵字是整數,一般合理的函數就是之間返回Key mod TableSize。
如果關鍵字是字符串,散列函數需要仔細選擇。如下一種↓

public static int hash(String key,int tableSize) {
        int hashVal=0;
        for(int i=0;i<key.length();i++)
            //hashVal+=key.charAt(i);
            hashVal=37*hashVal+key.charAt(i);
        hashVal=hashVal%tableSize;
        return hashVal<0?hashVal+tableSize:hashVal;
    }

例如”call”的哈希值,字符串c對應的unicode爲99,a對應的unicode爲97,L對應的unicode爲108,所以字符串”call”的散列值爲:

h = s[0] · 31L–1 + … + s[L – 3] · 312 + s[L – 2] · 311 + s[L – 1] · 310
3045982 = 99·313 + 97·312 + 108·311 + 108·310 = 108 + 31· (108 + 31 · (97 + 31 · (99)))

選擇一個好的散列函數,剩下就要解決衝突問題,最簡的兩種:分離鏈接法和開放定址法。

3. 分離鏈接發
分離鏈接法(separate chaining),將散列到同一個值的所有元素保留到一個雙向鏈接表中。

4. 開放定址法
探測散列表(probing hash table),不使用分離鏈接的散列表,其填裝因子低於λ=0.5。

  1. 線性探測法

函數f是i的線性函數,典型情形是f(i)=i。尋找一個自由單元,把它放入這個空閒的開放地址中。費時間。

char[] s = str.ToCharArray();
int hash = 0;
for (int i = 0; i < s.Length; i++)
{
hash = s[i] + (31 * hash);
}
return hash;

  1. 平方探測法

消除線性探測中一次聚集的衝突解決方法。衝突函數是二次探測方法。典型情形是f(i)=i²。尋找i²的空閒地址填入。需要表的一半地址作備選,費空間。

  1. 雙散列

雙散列(double hashing)是將f(i)當作目標值,第一個散列函數,i作爲一個尋找開放地址,第二個散列函數。典型的情形是f(i)=i·hash₂(x),hash₂(x)=x mod 9或者hash₂(x)=R-(x mod R)。理論可行,保證時間和空間。
具體代碼參考博客:http://blog.csdn.net/u012124438/article/details/78230478

PS:負載因子
散列表中的鍵值對數與容量的比值叫做負載因子(load factor)。通常負載因子越小,我們進行查找所需時間就越短,而空間的使用就越大;若負載因子較大,則查找時間會變長,但是空間使用會減小。比如,Java標準庫中的HashMap就是基於拉鍊法實現的散列表,它的默認負載因子爲0.75。HashMap實現動態調整容量的方式是基於公式loadFactor = maxSize / capacity,其中maxSize爲支持存儲的最大鍵值對數,而loadFactor和capacity(容量)都會在初始化時由用戶指定或是由系統賦予默認值。當HashMap中的鍵值對的數目達到了maxSize時,就會增大散列表中的容量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章