衝突並非一件嚴重的事情,由於咱們能夠用一些方式去解決它html
解決衝突的方式有三種: 拉鍊法,線性探測法和再哈希法java
拉鍊法是基於鏈表實現的查找表去實現的,關於鏈表查找表能夠看下我以前寫的這篇文章:算法
編寫哈希函數數組
/** * @description: 根據輸入的鍵獲取對應的哈希值 */ private int hash (Key key) { return (key.hashCode() & 0x7fffffff) % M; }
public class SeparateChainingHashST<Key,Value> { private int M; // 數組的大小 private SequentialSearchST<Key, Value> [] st; // 鏈表查找表對象組成的數組 public SeparateChainingHashST (int M) { st= new SequentialSearchST [M]; this.M = M; // 初始化數組st中的鏈表對象 for (int i=0;i<st.length;i++) { st[i] = new SequentialSearchST(); } } /** * @description: 根據輸入的鍵獲取對應的哈希值 */ private int hash (Key key) { return (key.hashCode() & 0x7fffffff) % M; } /** * @description: 根據給定鍵獲取值 */ public Value get (Key key) { return st[hash(key)].get(key); } /** * @description: 向表中插入鍵值對 */ public void put (Key key, Value val) { st[hash(key)].put(key, val); } /** * @description: 根據給定鍵刪除鍵值對 */ public void delete (Key key) { st[hash(key)].delete(key); } }
public class SequentialSearchST<Key, Value> { Node first; // 頭節點 int N = 0; // 鏈表長度 private class Node { Key key; Value value; Node next; // 指向下一個節點 public Node (Key key,Value value,Node next) { this.key = key; this.value = value; this.next = next; } } public int size () { return N; } public void put (Key key, Value value) { for(Node n=first;n!=null;n=n.next) { // 遍歷鏈表節點 if(n.key == key) { // 查找到給定的key,則更新相應的value n.value = value; return; } } // 遍歷完全部的節點都沒有查找到給定key // 1. 建立新節點,並和原first節點創建「next」的聯繫,從而加入鏈表 // 2. 將first變量修改成新加入的節點 first = new Node(key,value,first); N++; // 增長字典(鏈表)的長度 } public Value get (Key key) { for(Node n=first;n!=null;n=n.next) { if(n.key.equals(key)) return n.value; } return null; } public void delete (Key key) { if (N == 1) { first = null; return ; } for(Node n =first;n!=null;n=n.next) { if(n.next.key.equals(key)) { n.next = n.next.next; N--; return ; } } } }
public class Test { public static void main (String args[]) { SeparateChainingHashST<String, Integer> hashST = new SeparateChainingHashST<>(16); hashST.put("A",1); // 插入鍵值對 A - 1 hashST.put("B",2); // 插入鍵值對 B - 2 hashST.delete("B"); // 刪除鍵值對 B - 2 System.out.println(hashST.get("A")); // 輸出 1 System.out.println(hashST.get("B")); // 輸出 null } }
public class LinearProbingHashST<Key, Value> { private int M; // 數組的大小 private int N; // 鍵值對對數 private Key [] keys; private Value [] vals; public LinearProbingHashST (int M) { this.M = M; keys = (Key []) new Object[M]; vals = (Value[]) new Object[M]; } /** * @description: 獲取哈希值 */ private int hash (Key key) { return (key.hashCode() & 0x7fffffff) % M; } /** * @description: 插入操做 */ public void put (Key key, Value val) // 具體代碼下文給出 /** * @description: 根據給定鍵獲取值 */ public Value get (Key key) // 具體代碼下文給出 /** * @description: 刪除操做 */ public void delete (Key key) // 具體代碼下文給出 }
/** * @description: 調整數組大小 */ private void resize (int max) { Key [] temp = (Key [])new Object[max]; for (int i =0;i<keys.length;i++) { temp[i] = keys[i]; } keys = temp; } /** * @description: 插入操做 */ public void put (Key key, Value val) { // 當鍵值對數量已經超過數組一半時,將數組長度擴大一倍 if(N>(M/2)) resize(2*M); // 計算哈希值,求出鍵的位置 int i = hash(key); // 判斷該位置鍵是否爲空 while(keys[i]!=null) { if(key.equals(keys[i])) { // 該位置的鍵和給定key相同,則更新對應的值 vals[i] = val; return; } else { // 該位置的鍵和給定key不一樣,則檢查下一個位置的鍵 i = (i+1) % M; } } // 該位置鍵爲空則插入鍵值對 keys[i] = key; vals[i] = val; N++; return; }
簡單思考下就能明白爲何隨着鍵值對佔數組長度的比例的增長, 哈希表的性能會降低: 由於在這個過程當中,將更容易造成長的鍵簇(一段連續的非空鍵的組合)。而哈希表的查找/插入等通常都是遇到空鍵才能結束, 所以,長鍵簇越多,查找/插入的時間就越長,哈希表的性能也就越差數據結構
/** * @description: 根據給定鍵獲取值 */ public Value get (Key key) { for (int i=hash(key);keys[i]!=null;i=(i+1)%M) { if (key.equals(keys[i])) { return vals[i]; } } return null; }
/** * @description: 刪除操做 */ public void delete (Key key) { // 給定鍵不存在,不進行刪除 if (get(key) == null) return ; // 計算哈希值, 求得鍵的位置 int i = hash(key); // 獲取給定鍵的下標 while (!key.equals(keys[i])) { i = (i+1) % M; } // 刪除鍵值對 keys[i] = null; vals[i] = null; // 對被刪除鍵後面鍵簇的全部鍵都進行刪除並從新插入 i = (i+1)%M; while (keys[i]!=null) { Key redoKey = keys[i]; Value redoVal = vals[i]; keys[i] = null; vals[i] = null; put(redoKey,redoVal); i = (1+1) % M; } N--; }
public class LinearProbingHashST<Key, Value> { private int M; // 數組的大小 private int N; // 鍵值對對數 private Key [] keys; private Value [] vals; public LinearProbingHashST (int M) { this.M = M; keys = (Key []) new Object[M]; vals = (Value[]) new Object[M]; } /** * @description: 獲取哈希值 */ private int hash (Key key) { return (key.hashCode() & 0x7fffffff) % M; } /** * @description: 調整數組大小 */ private void resize (int max) { Key [] temp = (Key [])new Object[max]; for (int i =0;i<keys.length;i++) { temp[i] = keys[i]; } keys = temp; } /** * @description: 插入操做 */ public void put (Key key, Value val) { // 當鍵值對數量已經超過數組一半時,將數組長度擴大一倍 if(N>(M/2)) resize(2*M); // 計算哈希值,求出鍵的位置 int i = hash(key); // 判斷該位置鍵是否爲空 while(keys[i]!=null) { if(key.equals(keys[i])) { // 該位置的鍵和給定key相同,則更新對應的值 vals[i] = val; return; } else { // 該位置的鍵和給定key不一樣,則檢查下一個位置的鍵 i = (i+1) % M; } } // 該位置鍵爲空則插入鍵值對 keys[i] = key; vals[i] = val; N++; return; } /** * @description: 根據給定鍵獲取值 */ public Value get (Key key) { for (int i=hash(key);keys[i]!=null;i=(i+1)%M) { if (key.equals(keys[i])) { return vals[i]; } } return null; } /** * @description: 刪除操做 */ public void delete (Key key) { // 給定鍵不存在,不進行刪除 if (get(key) == null) return ; // 計算哈希值, 求得鍵的位置 int i = hash(key); // 獲取給定鍵的下標 while (!key.equals(keys[i])) { i = (i+1) % M; } // 刪除鍵值對 keys[i] = null; vals[i] = null; // 對被刪除鍵後面鍵簇的鍵的位置進行刪除並從新插入 i = (i+1)%M; while (keys[i]!=null) { Key redoKey = keys[i]; Value redoVal = vals[i]; keys[i] = null; vals[i] = null; put(redoKey,redoVal); i = (1+1) % M; } N--; } }
public class Test { public static void main (String args[]) { LinearProbingHashST<String, Integer> lst = new LinearProbingHashST<>(10); lst.put("A",1); lst.put("B",2); lst.delete("A"); System.out.println(lst.get("A")); // 輸出null System.out.println(lst.get("B")); // 輸出 2 } }