-
- 散清單
- 散列函數
- 基于拉鍊法的散清單
- 基于線性探測法的散清單
散清單
- 目的:如果所有的鍵都是小整數,可以用一個數組來實作無序的符号表,将鍵作為數組的索引而數組中鍵
處存儲的就是它對應的值。i
- 查找算法步驟
- 用散列函數将查找的函數轉化為數組的一個索引。理想情況下,不同的減可以轉換為不同的索引值。
- 處理碰撞沖突,分為拉鍊法和線性探測法。
- 散清單是算法在時間和空間上的均衡處理方式。
散列函數
- 散列函數特點:散列函數應該易于計算并且能夠均勻分布所有的鍵,即對于任意的鍵,所給出的散列值應得呈随機性。
- 散列函數與鍵的類型有關。
- 正整數:正整數散列最常用的方式為除留餘數法。選擇一個
的素數的數組,對于任意正整數M
,計算k
。如果不是素數,可能無法獲得均勻的散列值。k%M
- 浮點數:如果鍵值是0到1之間,可以将鍵值乘以素數M後,四舍五入得到一個0到M-1之間的索引值。但這樣會使鍵的高位起作用更大,低位鍵值不影響散列值。解決辦法是,鍵值用二進制表示後使用除留餘數法。
- 字元串:字元串散列同樣使用除留餘數法,将字元串考慮為大整數即可。Java的
函數能夠傳回一個非負16位整數。如果R是比任何字元都大的整數,相當于,将字元串當做一個N位的R進制值,将它除以M求餘。Horner方法,用N次乘法、加法和取餘來計算一個字元串的散列值。隻要R足夠小,不造成溢出。charAt()
java int hash = 0; for (int i = 0; i < s.length(); i++) hash = (R * hash + s.charAt(i)) % M
- 組合鍵:如果鍵的類型包含多個整形變量,我們可以和String類型一樣将他們混合起來。例如,被查找的類型是Date,其中整型域:day(兩位數)、month(兩位數)和year(四位數),散列值是:
Java int hash = (((day * R + month) % M) * R + year) % M;
- 隻要R足夠小不造成溢出,也可以得到0到M-1之間的散列值。同時選取适當素數M值,例如31,來省去括号内的
計算。%M
- 隻要R足夠小不造成溢出,也可以得到0到M-1之間的散列值。同時選取适當素數M值,例如31,來省去括号内的
- 正整數:正整數散列最常用的方式為除留餘數法。選擇一個
- Java約定:即散列值的硬性規則。由于每種資料類型都需要相應的散列函數,于是Java讓所有資料類型都繼承了一個能夠傳回32bit整數的hashCode()方法。是以若
的值為a.equals(b)
,那麼對應的hashCode值也應相同,若hashCode值不同,隻能說明看似相同的資料,實則為不同資料類型。但同hashCode值,兩個對象可能不同。ture
- 将hashCode的傳回值轉化為數組索引:若我們想使用短的索引值,而不是32bit整數的散列值,這裡利用
方法和除留餘數法結合,産生0到M-1的整數。hashCode()
private int hash(Key x){
return (x.hashCode() & ) % 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 = ;
hash = * hash + who.hashCode();
hash = * hash + when.hashCode();
hash = * hash + ((Double) amount).hashCode();
return hash;
}
...
}
- 軟緩存:如果散列值的計算很耗時,可以将計算的散列值存儲在每個鍵的散列值函數裡,即利用hash變量存儲hashCode()的傳回值。