教你從零開始寫一個哈希表--附錄

附錄:其他的衝突處理方案

常見的兩種哈希衝突解決方案如下:

  • 鏈表法
  • 開放尋址法

鏈表法

  分離鏈表法中,每一個桶包含一個鏈接表。當鍵值對的鍵衝突時,鍵值對會被加入到這個列表中。它支持的方法如下:

插入:
  計算關鍵字的哈希值來查找桶的下表索引。如果訪問的桶沒有值,把鍵值對插入到這個桶中。如果已經保存了鍵值對了,將待插入的鍵值對追加到鏈接表後面。
搜索:
  計算關鍵字的哈希值來查找桶的下表索引。遍歷鏈接表,用待查找的關鍵字跟比較每一個鍵值對的關鍵字。如果找到了關鍵字,返回對應的值,否則返回空。
刪除:
  計算關鍵字的哈希值來查找桶的下表索引。遍歷鏈接表,用待查找的關鍵字跟比較每一個鍵值對的關鍵字。如果找到了關鍵字,從鏈接表中刪除對應的鍵值對。如果鏈接表中只有一個鍵值對了,在桶中放一個空值以標誌鏈接表是空的。

  這個方法的優勢是易於實現,但是空間效率低下。每一個鍵值對都保存了所在鏈接表中的下一個節點的指針。如果沒有下一個節點保存空指針。空間浪費在了記錄指針上面,這本可以用來存儲更多鍵值對的。

開放尋址法

  開放尋址法解決了鏈表法的空間效率低的問題。當發生衝突時,衝突的鍵值對被放在哈希表的其他桶中。放置鍵值對的桶,是依據預設好的規則來選擇的。這樣,查找鍵值對的時候可能出現重複。目前有三種常見的方法來爲衝突的鍵值對選擇選擇可插入的桶。

線性探測

  當發生衝突時,增加下標,然後把鍵值對放在數組的下一個可用的桶中。方法如下:

插入:
  計算關鍵字的哈希值來查找桶的下標索引。如果桶是空的,把鍵值對插入的這裏。如果桶是非空的,重複增加下表的動作,直到找到空的桶,然後把鍵值對插入到這個桶中。
搜索:
  計算關鍵字的哈希值來查找桶的下表索引。重複增加下標,比較每一個鍵值對的關鍵字跟待查找的關鍵字,直到找到一個空的桶。如果匹配到了待查詢的關鍵字,返回對應的值,否則返回空。
刪除:
  計算關鍵字的哈希值來查找桶的下表索引。重複增加下標,比較每一個鍵值對的關鍵字跟待刪除的關鍵字,直到找到一個空的桶。如果匹配到了待查詢的關鍵字,刪除對應的鍵值對。刪除一個鍵值對會使鏈表斷開,我們只能把待刪除的鍵值對後面的所有節點插入到鏈表的後面。

  線性探查提供了很好的緩存性能,但是導致了擴展性的問題。把衝突的鍵值對放在下一個可用的桶中,這可能會導致以填充的桶的連續擴張。插入、搜索或刪除時,這種做法都需要遍歷。

二次探測

  類似於線性探查,但它並沒有把衝突的項放到下一個可用的桶中,而是嘗試着放進下標爲i, i + 1, i + 4, i + 9, i + 16, ...,序列的桶中。此處的i是原先的關鍵字哈希值。支持的方法如下:

插入:
  計算關鍵字的哈希值來查找桶的下表索引。遍歷探查序列,直到發現了空的或者已被刪除的桶,隨之將鍵值對插入找到的桶中。
搜索:
  計算關鍵字的哈希值來查找桶的下表索引。遍歷探查序列,直到發現了空的或者已被刪除的桶,否則比較序列對應的桶中鍵值對的關鍵字和待查找的關鍵字;如果關鍵字匹配,返回對應的值,反之返回空。
刪除:
  我們無法辨別待刪除的項是否處於衝突鏈表中,因此無法立刻刪除該項。我們只能將其標記爲“已刪除”。

  儘管二次探查法沒有根除鍵值對的聚集,但是情況有所緩解;同時它也提高了存儲的性能。

再哈希法

  再哈希法是要解決鍵值對聚集問題的。爲此,我們使用備用哈希函數來爲鍵值對計算新的索引位置。使用哈希函數給出新的桶,桶的下標得是全局均勻分佈的。這個方法解決鍵值對聚集問題,同時也增強了存儲的性能。再哈希法是生產中常見的衝突解決方法。教程中也實現了這個方法。

上一篇:教你從零開始寫一個哈希表–調整大小

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