散列表根據關鍵碼直接訪問表,把關鍵碼映射到表中的記錄來訪問記錄,這個過程成爲散列(hashing)
java
把關鍵碼值映射到位置的函數成爲散列函數(hash function),用h表示數據庫
存放記錄的數組稱爲散列表(hash table),用HT表示數組
散列表中的一個位置被稱爲一個槽(slot),散列表HT中的槽的數目用變量M表示,從0到M-1編號函數
設計散列方法的目標是使得對於任意關鍵碼值K和某個散列函數h,0<=h(K)<=M-1,有HT[i]=Kspa
在一個根據散列方法組織的數據庫中,找到帶有關鍵碼值K的記錄有兩個過程設計
一、計算表的位置h(k)code
二、從槽h(k)開始,使用衝突解決策略找到包含關鍵碼值的記錄排序
爲何使用散列函數?記錄關鍵碼的範圍很大,把記錄存放在一個槽數相對較少的表中,兩條記錄映射到表中的同一個槽就會發生衝突。衝突不可避免。通常咱們但願選擇的散列函數能夠把記錄用相同的機率分佈到散列表的全部槽中。字符串
散列函數舉例hash
一、將整數散列到有16個槽的表中
int h(int x) { return(x % 16); }
散列函數的值依賴於關鍵碼低四位
二、平方取中法,適用於數
計算關鍵碼值的平方,取中間R位,關鍵碼值的大多數位或全部位都對結果有貢獻
三、字符串散列函數
int h(String x, int M) { char ch[]; ch = x.toCharArray(); int xlength = x.length(); int i, sum; for (sum=0, i=0; i<x.length(); i++) sum += ch[i]; return sum % M; }
將字符串全部ASCII累加,對全部字符賦予一樣的權重
衝突解決技術分爲兩類:開散列方法(單鏈方法) 閉散列方法(開地址方法)
開散列方法的一種簡單形式把散列表的每一個槽定義爲一個鏈表的表頭,散列到一個槽中的全部記錄都放在該槽的鏈表內。
鏈表中的記錄能夠按照插入次序排列,或者按照關鍵碼值次序排列。
按照關鍵碼排序的狀況,若是遇到一個比檢索值大的關鍵碼就應該中止檢索。而按照插入次序就須要遍歷鏈表每個記錄。
閉散列方法把全部記錄直接存儲在散列表中,每條記錄有一個基位置(home position)h(k),即由散列函數計算出來的槽。若是插入一條記錄R而另一條記錄已經佔據了R的基位置,就把R存儲在表的其餘槽,由衝突解決策略肯定應該是哪個槽。檢索應該遵循一樣的策略。
把散列表中的M個槽分爲B個桶(bucket),每一個桶包含M/B個槽。散列函數把每一條記錄分配到桶中的第一個槽中,若是該槽已經被佔用就順序沿桶查找知道找到一個空槽。若是一個桶所有被佔滿了就把記錄存儲到表後面具備無限容量的溢出桶中。全部桶共享一個溢出桶。
衝突解決策略:能夠產生一組可能放置記錄的散列表的槽,第一個位置被佔用,衝突解決策略就會達到這個組的下一個槽,以此類推直到找到一個空槽。這組槽被稱爲探測序列。
/ ** Insert record r with key k into HT * / void hashInsert(Key k, E r) { int home; // Home position for r int pos = home = h(k); // Initial position for (int i=1; HT[pos] != null; i++) { pos = (home + p(k, i)) % M; // Next pobe slot assert HT[pos].key().compareTo(k) != 0 : "Duplicates not allowed"; } HT[pos] = new KVpair<Key,E>(k, r); // Insert R } / ** Search in hash table HT for the record with key k * / E hashSearch(Key k) { int home; // Home position for k int pos = home = h(k); // Initial position for (int i = 1; (HT[pos] != null) && (HT[pos].key().compareTo(k) != 0); i++) pos = (home + p(k, i)) % M; // Next probe position if (HT[pos] == null) return null; // Key not in hash table else return HT[pos].value(); // Found it }
p(k,i)是探查函數,爲關鍵碼R的探查序列第i個槽返回從基位置開始的偏移量。
最簡單的p(k,i)
p(k,i)=i mod M可能產生基本彙集
改進的探測函數
p(k, i) =(h(K) + ic) mod M,每次不是跳過1個槽而是跳過n個槽
定義節點
/** Container class for a key-value pair */ class KVpair<Key, E> { private Key k; private E e; /** Constructors */ KVpair() { k = null; e = null; } KVpair(Key kval, E eval) { k = kval; e = eval; } /** Data member access functions */ public Key key() { return k; } public E value() { return e; } }
定義hashtable
import java.io.*; public class HashTable<Key extends Comparable<? super Key>, E> { private int M; private KVpair<Key,E>[] HT; //hash映射函數 private int h(Key key) { return M - 1; } //線性探測函數 private int p(Key key, int slot) { return slot; } @SuppressWarnings("unchecked") // Generic array allocation HashTable(int m) { M = m; HT = (KVpair<Key,E>[])new KVpair[M]; } /** Insert record r with key k into HT */ void hashInsert(Key k, E r) { int home; // Home position for r int pos = home = h(k); // Initial position for (int i=1; HT[pos] != null; i++) { pos = (home + p(k, i)) % M; // Next pobe slot assert HT[pos].key().compareTo(k) != 0 : "Duplicates not allowed"; } HT[pos] = new KVpair<Key,E>(k, r); // Insert R } /** Search in hash table HT for the record with key k */ E hashSearch(Key k) { int home; // Home position for k int pos = home = h(k); // Initial position for (int i = 1; (HT[pos] != null) && (HT[pos].key().compareTo(k) != 0); i++) pos = (home + p(k, i)) % M; // Next probe position if (HT[pos] == null) return null; // Key not in hash table else return HT[pos].value(); // Found it } }