1. 程式人生 > >雜湊表應用

雜湊表應用

雜湊表-查詢O(1)複雜度的利器。

理論上說,當輸入規模夠大時是不可能沒有衝突的,因為兩個域要有對映,且結果域是遠遠小於輸入域的,選取一個好的雜湊函式固然重要,但是解決衝突的方法也是必不可少。

這裡先普及一些東西:雜湊函式,在隨機大樣本測試下,基於概率學的研究,每個桶內部的元素個數是差不多的。

為了避免衝突,我們有一些神奇的方法:

1)以int-4位元組資料為例,將資料轉換為二進位制,奇數位與偶數位分離,各自放在新的32位整形的低16位上,再異或。

2)選取2的次冪的位置,與剩下的二進位制進行異或。

3)對素數取模。

總之,只要操作方式足夠隨機,或是哪怕不隨機,但是選取的元素間毫無關係可言【例如二進位制下不同的位】,就可以稱為一個好的雜湊函式。

解決衝突的方式:

1.開放定址   雜湊到衝突位置後,給定可變或不變步長,向後尋找空位置。

2.連結串列法  在桶後面接上鍊表

優化:據說JVM裡面的hash,是在桶後面建紅黑樹

3.再hash  造出足夠多的hash函式,衝突則將hash(i)函式的結果用hash(i+1)函式再計算。

如何造出那麼多hash:多hash的前提是,hash不能與任意一個hash有相關性。

現假設我們有兩個非相關hash,hash1和hash2,我們可以用加權和的方式造出無窮個hash:

hash3=hash1+hash2,hash4=hash1+2*hash2,也就是hash(i)=i*hash1+hash2,hash3雖然看起來和hash1和hash2有關,但是由於hash1、hash2不相關,因此他們加權和的結果和各自都不存在相關性。

 

要求設計一種結構,增刪改查都可以做到O(1),並且還可以等概率返回其中的一個元素。

查詢要O(1)?那毫無疑問就是hash了,問題是hash如何做到等概率返回一個元素呢?

遍歷:O(n)。

遍歷的目的是什麼:我們需要知道有多少個元素。

遍歷如何實現等概率返回:遍歷的同時將資料打到一個數組A裡,然後用隨機函式rand()生成一個1~n【注:由於這個結構支援增刪,所以n是可變的】的index,然後返回A[index]。

避免遍歷:如果我們一開始就給這些元素index,會怎麼樣?

我們用一個hash來儲存index->value對,同時用size記錄下當前的最大索引,這樣rand()生成1~size之間的數的時候就不需要遍歷就可以知道長度了,同時,複雜度滿足O(1)的要求。

 

那麼刪除呢:我們的index是從1開始按照步長為1遞增的。我們很有可能把不是結尾元素的第i號元素刪除了,那麼這個時候i對應的value就會空掉,此時必須經過多次rand(),一旦刪除元素過多,由於index是不斷遞增的,碰到空的機率會越來越大,需要的rand次數就會變多。

實際上,當我們刪除一個元素造成空洞的時候,完全可以將最後的元素調整到空洞位置。

查詢由於我們給的是value,要查是否存在,所以必須要有一個hash寸的是value->index,而等概率返回則要求我們需要一個index->value的hash。

總結:設計hash1:value->index,hash2:index->value

查詢:直接用value在hash1內查詢即可

改動:給定一個value,在hash1中找到他的索引,再根據這個索引在hash2中找到它,改兩個hash的value域即可。

增加:hash1.insert(value,++size);hash2.insert(size,value)。

刪除:

A.刪除的是尾元素,直接刪除

B.得到hash2中size對應的value',將hash2和hash1中刪除位置的value變更為value'。