rknfish

rknfish

i need more love
github

第七講:雜湊表

參考筆記

參考投影片

參考課程

封面


1. 資料結構#

DBMS 主要包括以下幾種資料

  • 內部元數據:記錄有關數據庫和系統的信息
  • 核心數據存儲:記錄 tuple 的信息
  • 臨時數據結構
  • 表索引

主要考慮以下兩個問題

  1. 資料組織
  2. 資料的可靠性

2. 哈希表#

哈希表

哈希表目標

哈希函數

  1. 將值轉化為一個較小的鍵
  2. 速度碰撞率 之間進行權衡。

哈希方案

  1. 該如何處置碰撞的情形
  2. 需要在 哈希表大小額外信息維護之間 權衡

3. 哈希函數#

不管輸入的 key 是什麼,輸出一個 特別的 數字 的函數。

哈希函數

哈希函數基準


4. 靜態哈希方案#

4.1 線性探針哈希#

線性探針

我們解決哈希碰撞的方法是,如果我們的哈希函數返回的位置已經被佔用了,那麼則一直向下取一個位置,直到找到表中空的值。

在刪除操作中,可能會出現一種情形,有的值並不是 hash function 產生的直接值,而是通過一直取下一個位置得到的值。

在這種情形下,我們所採取的方法有。

  1. 將受到偏移影響的值往回調整。通過重新哈希的方式進行調整。效率比較低下。
  2. 將那些刪除了的值標記為已刪除。這些值可以在新的值需要使用的時候重新利用。不過需要周期性的垃圾回收。

存儲相同的值

非唯一鍵

存儲相同的 key 但有不同的 value 的值的時候,我們可以有兩種辦法解決。

  1. 對於每個 key 他們的 value 開闢出來一個單獨的空間
  2. 所有的 key 都用 哈希函數 散列在一個存儲空間中。

4.2 Robin Hood Hashing#

這是線性探針算法的一升級版,旨在解決分布不均的問題。在上面的算法中,可能存在一些 key,經過哈希計算後可能會聚集在一塊導致哈希算法劣化。 Robin Hood hashing 解決碰撞的方式會將部分 rich 的 key 分配給 poor key.

這種避免哈希碰撞的方法是,給每個值都記錄一個偏移值,表中的每個值在向下搜索的時候,需要和表中的值進行對比。如果當前偏移值比表中值的偏移值大,那麼則將當前值和表中值進行替換。直到找到空位結束。

void Robin_hood_Hashing(int key) {
     int val = hash(key);
     int offset = 0;
     for (int i = val ; ; i ++ , offset ++ ) {
          if(hash_table->is_empty(i)){ 
               hash_table->insert(i , val , offset);
               break;
          }

          if(hash_table->get_offset(i) < offset) {
               auto [n_val , n_offset] = hash_table->get_data(i);
               hash_table->insert(i , val , offset);
               val = n_val , offset = n_offset;
          }
          if(i == MAXSIZE) i = 0;
     }
}

4.3 Cuckoo Hashing#

這種解決碰撞的方法是使用兩種哈希函數和兩張哈希表完成的。

對於輸入的 key 他會計算出 val[0]val[1] 在不確定的情況下,會隨機采用其中一個作為最終的結果,當其中一個觸發碰撞的時候,則采用一個哈希函數與哈希表。如果兩個都碰撞的話,那麼則重新計算碰撞的哈希,直到沒有碰撞。

采用這正方法,在查詢的時候複雜度永遠都是 O(1)O(1) ,但插入的代價比加大。

開源 CMU 實現


5 動態哈希方案#

觀察

以上的的哈希表都基於固定大小的元素,所以需要一些動態的哈希表。

5.1 鏈式哈希#

對每個哈希值都維護一個鏈表。將所有哈希值相同的鍵都放在一個 bucket 中。bucket 是通過鏈表的形式連接在一起的。

鏈式哈希

5.2 擴展哈希#

在 bucket 快滿的時候,通過二進制前綴開闢新的 bucket。

分割

詳解

5.3 線性哈希#

運行時動態擴容的一種哈希。

詳解

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。