散列表.md

散列表

  • 目的:如果所有的鍵都是小整數,可以用一個數組來實現無序的符號表,將作爲數組的索引而數組中鍵i處存儲的就是它對應的值。
  • 查找算法步驟
    1. 散列函數將查找的函數轉化爲數組的一個索引。理想情況下,不同的減可以轉換爲不同的索引值。
    2. 處理碰撞衝突,分爲拉鍊法線性探測法
  • 散列表是算法在時間和空間上的均衡處理方式。

散列函數

  • 散列函數特點:散列函數應該易於計算並且能夠均勻分佈所有的鍵,即對於任意的鍵,所給出的散列值應得呈隨機性。
  • 散列函數與鍵的類型有關。
    1. 正整數:正整數散列最常用的方式爲除留餘數法。選擇一個M的素數的數組,對於任意正整數k,計算k%M。如果不是素數,可能無法獲得均勻的散列值。
    2. 浮點數:如果鍵值是0到1之間,可以將鍵值乘以素數M後,四捨五入得到一個0到M-1之間的索引值。但這樣會使鍵的高位起作用更大,低位鍵值不影響散列值。解決辦法是,鍵值用二進制表示後使用除留餘數法
    3. 字符串:字符串散列同樣使用除留餘數法,將字符串考慮爲大整數即可。Java的charAt()函數能夠返回一個非負16位整數。如果R是比任何字符都大的整數,相當於,將字符串當做一個N位的R進制值,將它除以M求餘。Horner方法,用N次乘法、加法和取餘來計算一個字符串的散列值。只要R足夠小,不造成溢出。
      java
      int hash = 0;
      for (int i = 0; i < s.length(); i++)
      hash = (R * hash + s.charAt(i)) % M
    4. 組合鍵:如果鍵的類型包含多個整形變量,我們可以和String類型一樣將他們混合起來。例如,被查找的類型是Date,其中整型域:day(兩位數)、month(兩位數)和year(四位數),散列值是:
      Java
      int hash = (((day * R + month) % M) * R + year) % M;

      • 只要R足夠小不造成溢出,也可以得到0到M-1之間的散列值。同時選取適當素數M值,例如31,來省去括號內的%M計算。
  • Java約定:即散列值的硬性規則。由於每種數據類型都需要相應的散列函數,於是Java讓所有數據類型都繼承了一個能夠返回32bit整數的hashCode()方法。因此若a.equals(b)的值爲ture,那麼對應的hashCode值也應相同,若hashCode值不同,只能說明看似相同的數據,實則爲不同數據類型。但同hashCode值,兩個對象可能不同。
  • 將hashCode的返回值轉化爲數組索引:若我們想使用短的索引值,而不是32bit整數的散列值,這裏利用hashCode()方法和除留餘數法結合,產生0到M-1的整數。
  private int hash(Key x){
      return (x.hashCode() & 0x7ffffffff) % M;
  }
  • 自定義的hashCode()方法:可以按照將hashCode的返回值轉化爲短整數的方法,進行變換。將對象的每個變量的hashCode()返回值轉化爲32位整數並計算得到散列值。
public class Transaction{
    ...
    private final String who;
    private final Date when;
    private final double amount;

    public int hashCode(){
    int hash = 17;
    hash = 31 * hash + who.hashCode();
    hash = 31 * hash + when.hashCode();
    hash = 31 * hash + ((Double) amount).hashCode();
    return hash;
    }
    ...
} 
  • 軟緩存:如果散列值的計算很耗時,可以將計算的散列值存儲在每個鍵的散列值函數裏,即利用hash變量存儲hashCode()的返回值。

基於拉鍊法的散列表

基於線性探測法的散列表

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