哈希表(散列表)算法
經過哈希函數使元素的存儲位置與它 的關鍵碼之間可以創建一一映射的關係,在查找時能夠很快找到該元素。 數組
哈希表hash table(key,value) 的作法其實很簡單,就是把Key經過一個固定的算法函數既所謂的哈希函數轉換成一個整型數字,而後就將該數字對數組長度進行取餘,取餘結果就看成數組的下標,將value存儲在以該數字爲下標的數組空間裏。函數
1.哈希衝突:性能
就是鍵(key)通過hash函數獲得的結果做爲地址去存放當前的鍵值對,可是卻發現該地址已經有人先來了就會產生衝突。這個衝突就是hash衝突了。this
2.load factoe 裝載因子:spa
已佔用桶的個數/桶的總數code
當裝載因子大於 0.8時就應該擴容。 blog
因爲哈希衝突的存在形成哈希表的增刪查時間複雜度只能無限趨近於0(1)內存
3. 哈希衝突的解決:rem
1.能夠把key存放在表中的「下一個「」空位置。即從發生衝突的位置開始,依次向後探測,直到找到空位置爲止(線性探測)。
2.實現鏈式哈希表,從根本上說是由一組鏈表構成。每一個鏈表均可以看作是一個「桶」,咱們將全部的元素經過散列的方式放到具體的不一樣的桶中。插入元素時,首先將其鍵傳入一個哈希函數(該過程稱爲哈希鍵),函數經過散列的方式告知元素屬於哪一個「桶」,而後在相應的鏈表頭插入元素。查找或刪除元素時,用一樣的方式先找到元素的「桶」,而後遍歷相應的鏈表,直到發現咱們想要的元素。由於每一個「桶」都是一個鏈表,因此鏈式哈希表並不限制包含元素的個數。然而,若是表變得太大,它的性能將會下降。
3.優勢:增刪改查O(1)
缺點:1.佔用內存比較大
2.元素沒有任何順序
4.源代碼:
線性探測哈希表
class LinerHashMap<T extends Comparable<T>>{ // 散列表數組 private Entry<T>[] hashTable; // 被佔用的桶的個數 private int usedBucketNum; // 哈希表的裝載因子 private double loadFactor; // 定義素數表 private static int[] primTable; // 記錄當前使用的素數的下標 private int primIndex; // 類的靜態初始化塊 static{ primTable = new int[]{3, 7, 23, 47, 97, 127}; } /** * 構造函數,初始化 */ public LinerHashMap(){ this.primIndex = 0; this.hashTable = new Entry[primTable[this.primIndex]]; this.usedBucketNum = 0; this.loadFactor = 0.75; } /** * 增長元素 * @param key */ public void put(T key){ // 計算哈希表是否須要擴容 double ret = this.usedBucketNum*1.0 / this.hashTable.length; if(ret > this.loadFactor){ resize(); // 哈希表的擴容 } // 先計算key應該放的桶的下標 int index = key.hashCode() % this.hashTable.length; int idx = index; do{ // 表示是從未使用過的桶 if(this.hashTable[idx] == null){ this.hashTable[idx] = new Entry<>(key, State.USING); this.usedBucketNum++; return; } // 表示使用過的桶 if(this.hashTable[idx].getState() == State.USED){ this.hashTable[idx].setData(key); this.hashTable[idx].setState(State.USING); this.usedBucketNum++; return; } else { // 正在使用中的桶,不插入重複元素 if(this.hashTable[idx].getData().compareTo(key) == 0){ return; } } idx = (idx+1)%this.hashTable.length; } while(idx != index); } /** * 哈希表的擴容函數 */ private void resize() { Entry<T>[] oldHashTable = this.hashTable; this.hashTable = new Entry[primTable[++this.primIndex]]; this.usedBucketNum = 0; for (int i = 0; i < oldHashTable.length; i++) { if(oldHashTable[i] != null && oldHashTable[i].getState() == State.USING){ this.put(oldHashTable[i].getData()); this.usedBucketNum++; } } } /** * 刪除元素 * @param key */ public void remove(T key){ // 先計算key應該放的桶的下標 int index = key.hashCode() % this.hashTable.length; // 從當前位置開始找元素 int idx = index; do{ // 若是遍歷桶的過程當中,發現了從未使用過的桶,直接返回 if(this.hashTable[idx] == null){ return; } if(this.hashTable[idx].getState() == State.USING && this.hashTable[idx].getData().compareTo(key) == 0){ this.hashTable[idx].setData(null); this.hashTable[idx].setState(State.USED); this.usedBucketNum--; return; } idx = (idx+1)%this.hashTable.length; } while(idx != index); } /** * 查詢元素 返回key的值,找不到返回null * HashMap * @param key * @return */ public T get(T key){ // 先計算key應該放的桶的下標 int index = key.hashCode() % this.hashTable.length; // 從當前位置開始找元素 int idx = index; do{ // 若是遍歷桶的過程當中,發現了從未使用過的桶,直接返回 if(this.hashTable[idx] == null){ return null; } if(this.hashTable[idx].getState() == State.USING && this.hashTable[idx].getData().compareTo(key) == 0){ return key; } idx = (idx+1)%this.hashTable.length; } while(idx != index); return null; } /** * 定義桶的狀態值 */ static enum State{ UNUSE,// 桶從未使用過 USED,// 桶被用過了 USING// 桶正在使用中 } /** * 定義桶的元素類型 * @param <T> */ static class Entry<T extends Comparable<T>>{ T data; State state; public Entry(T data, State state) { this.data = data; this.state = state; } public T getData() { return data; } public void setData(T data) { this.data = data; } public State getState() { return state; } public void setState(State state) { this.state = state; } } }
鏈式探測哈希表
public class LinkHashTable<K extends Comparable<K>,V> { // 哈希桶 private Entry<K,V>[] table; // 裝載因子 0.75 private double loadFactor; // 記錄已經佔用的桶的數量 private int usedBucketSize; /** * 哈希表初始化 */ public LinkHashTable(){ this.table = new Entry[3]; this.loadFactor = 0.75; this.usedBucketSize = 0; } /** * 給哈希表增長元素 * @param key * @param value */ public void put(K key, V value){ if(this.usedBucketSize*1.0/this.table.length>this.loadFactor){ this.expand(); } int idx = key.hashCode() % this.table.length; if(this.table[idx]==null){ this.table[idx]=new Entry<>(key,value,null); this.usedBucketSize++; return; } Entry<K,V>entry=this.table[idx]; //判斷是否有這個key,若是有直接替換 while (entry!=null){ if(entry.key.compareTo(key)==0){ entry.value=value; return; } entry=entry.next; } this.table[idx]=new Entry<>(key,value,this.table[idx]); } /** * 在哈希表中查詢key是否存在,若是key存在,返回它對應的value值, * 不然返回null * @param key * @return */ public V get(K key){ int idx = key.hashCode() % this.table.length; if(this.table[idx]==null){ return null; } Entry<K,V>entry=this.table[idx]; while (entry!=null){ if(entry.key.compareTo(key)==0){ return entry.value; } entry=entry.next; } return null; } /** * 刪除哈希表中key值爲參數指定的節點 * @param key */ public void remove(K key){ int index = key.hashCode() % this.table.length; if(this.table[index]==null){ return; }else if(this.table[index].key.compareTo(key)==0){ this.table[index]=this.table[index].next; return; } Entry<K,V>entry=this.table[index]; Entry<K,V>entry1=entry.next; if(entry1!=null){ if(entry1.key.compareTo(key)==0){ entry.next=entry1.next; } entry=entry.next; entry1=entry1.next; } if(this.table[index]==null){ this.usedBucketSize--; } } /** * 哈希表的擴容函數 */ private void expand() { Entry<K, V>[] oldTable = this.table; this.table = new Entry[oldTable.length * 2 + 1]; this.usedBucketSize = 0; for (int i = 0; i < oldTable.length; i++) { if (oldTable[i] != null) { this.put(oldTable[i].key, oldTable[i].value); } } } /** * 鏈式哈希表中節點的類型 * @param <K,V> */ static class Entry<K extends Comparable<K>,V> { K key; // student id V value; // student Entry<K, V> next; public Entry(K key, V value, Entry<K, V> next) { this.key = key; this.value = value; this.next = next; } } }