1. 程式人生 > >資料結構和演算法分析: 第五章 雜湊

資料結構和演算法分析: 第五章 雜湊

散列表的實現常常叫做雜湊。雜湊是一種用於以常數平均時間執行插入、刪除和查詢的技術。

5.1 一般想法

散列表的資料結構是一個包括一些項(item)的具有固定大小的陣列。通常查詢是對於項的某個部分(即資料域)來進行的。這部分就叫做關鍵字

每個關鍵字被對映到0到TableSize-1這個範圍的某個數字中,並且被放到適當的單元中。這個對映函式就叫做雜湊函式(hash function)。

5.2 雜湊函式

  • 如果輸入的關鍵字是整形,則一般的合理的方法就是直接返回Key mod TableSize,除非Kye碰巧具有某些不合乎需要的性質。
  • 通常,關鍵字是字串。一種選擇方法就是把字串的ASCII 碼都加起來。但是這種雜湊函式,不是一種均勻的分配。
  • 根據Horner法則計算一個(37)的多項式函式。列如計算 hk= k0 + 37*k1 +sqrt(37)*k2的方式計算雜湊值。

在這裡插入圖片描述

解決衝突問題:如果一個元素被插入時與另一個已經插入的元素雜湊到同一個位置,那麼久產生了一個衝突,這個衝突需要消除。解決衝突的方法主要有兩種:一個是分離連結法和開放地址法。

5.3 分離連結法

分離連結法:其做法就是將雜湊到同一個值的所有元素保留到一個表中。

為執行一次查詢,我們使用雜湊函式來確定究竟要遍歷到哪個連結串列,然後我們再在被確定的連結串列中執行一次查詢。如果這個元素是一個新元素,那麼它將被插入到連結串列的前端,這不僅方便,還因為常常發生這樣的事實:新進插入的元素最有可能不久後就被訪問。

在這裡插入圖片描述

我們定義散列表的裝填因子 λ為散列表中元素的數量對該散列表的大小的比值。連結串列的平均長度為λ。執行一次查詢所需要的時間是計算雜湊函式值所需要的常數時間加上遍歷連結串列所需要的時間。

分離連結雜湊法的一般法則是使得表的大小與預料的元素個數大致相當(換句話說λ≈1)。

5.4 不用連結串列的散列表

在這裡插入圖片描述

5.4.1 線性探測表

線上性探測方法中,函式f是i的線性函式,典型情形是f(i)=i。

  • 缺點:即使表相對較空,這樣佔據的單元也會開始形成一些區塊,其結果成為一次聚集。就是說,雜湊到區塊中的關鍵字都需要多次試選單元才能解決衝突,安徽關鍵字被新增到相應的區塊中去。

5.4.2 平方探測法

平凡探測法是消除線性探測中一次聚集問題的衝突解決方法。平方探測就是衝突函式為二次的探測方法。流行的選擇就是f(i)=sqrt(i)。

  • 缺點:對於線性探測,讓散列表幾乎都填滿元素並不是一個好主意,因此標的效能會降低。對於平方探測情況甚至更糟:一旦被填充超過一半,當表大小不是素數的時甚至在表被填充一半之前,就不能保證一次找到空的單元了。這是因為最多有表的一半可以用作解決衝突的備選位置。

5.4.3 雙雜湊

在這裡插入圖片描述

表的大小最好設定為素數

5.5 再雜湊

對於使用探測散列表來說,如果散列表被填的太滿,那麼操作的執行時間將開銷消耗過大,且插入可能失敗。這可能發生有太多的移動和插入混合的場合。此時,一種解決方法就是建立另外一個大約兩倍大的表(而且使用一個相關的新雜湊函式),掃描整個原始表列,計算每一個元素的新的雜湊值並將其插入到新表中去。

整個操作就叫做再雜湊。

再雜湊可以用平方探測以多種方式實現。

  1. 只要表被填滿到一半就再雜湊。
  2. 只有當插入失敗的時候再雜湊。
  3. 途中策略:當散列表到達某一個裝填因子時進行再雜湊

5.6 標準庫中的散列表

HashSet和HashMap通常是採用分離連結法實現的。其中HashSet中的項(或者是HashSet中的關鍵字以及HashMap中的關鍵字)必須通過equals方法和HashCode方法。