解決hash衝突的4種方法

一)哈希表簡介

 

非哈希表的特色:關鍵字在表中的位置和它之間不存在一個肯定的關係,查找的過程爲給定值一次和各個關鍵字進行比較,查找的效率取決於和給定值進行比較的次數。html

    哈希表的特色:關鍵字在表中位置和它之間存在一種肯定的關係。java

哈希函數:通常狀況下,須要在關鍵字與它在表中的存儲位置之間創建一個函數關係,以f(key)做爲關鍵字爲key的記錄在表中的位置,一般稱這個函數f(key)爲哈希函數。算法

hash : 翻譯爲「散列」,就是把任意長度的輸入,經過散列算法,變成固定長度的輸出,該輸出就是散列值。數組

           這種轉換是一種壓縮映射,散列值的空間一般遠小於輸入的空間,不一樣的輸入可能會散列成相同的輸出,因此不可能從散列值來惟一的肯定輸入值。app

           簡單的說就是一種將任意長度的消息壓縮到莫伊固定長度的消息摘要的函數。函數

hash衝突:(大師兄本身寫的哦)就是根據key即通過一個函數f(key)獲得的結果的做爲地址去存放當前的key value鍵值對(這個是hashmap的存值方式),可是卻發現算出來的地址上已經有人先來了。就是說這個地方要擠一擠啦。這就是所謂的hash衝突啦測試

二)哈希函數處理衝突的方法

1)開放定址法:

其中 m 爲表的長度spa

對增量di有三種取法:.net

線性探測再散列   di = 1 , 2 , 3 , ... , m-1翻譯

平方探測再散列   di = 1 2 , -12 , 22 , -22 , 32 , -32 , ... , k2 ,  -k2

(大師兄備註:嗎單,上面的平方探測再散列是加1的平方;減1的平方,加2的平方,減2的平方,加3的平方,減3的平方。。。加k的平方,減k的平方。臥擦,老師你能再坑點麼?法科。要是你直接看這個平方探測再散列的di是怎麼來的,不必定能看懂老師ppt的這個寫法,是平方的意思。上面的紅色字呢,至關因而老師的ppt,是對應上面的圖片一塊兒看的。)

隨機探測再散列   di 是一組僞隨機數列

例子:

我在上面的這個配圖底部寫的那個紅色的12,我當時測試的時候,不知道這個12,也就是上面增量 di 的由來。不知道,限制知道了,那是1的2次方。。。。老師懶得或者說不會給數字打角標。

2)鏈地址法

上面這個只是老師的ppt,下面放上本身親自整的測試。

先按照ppt上的hash算法:h(key) = key % 7,算出來對應的hash值,這個hash值暫時就決定,當前的這個值,存放在數組的位置。
都算完以後,就能夠,按照這個hash值,依次的,把這些數,都放在下面的數組上。而後就有我本身的這個截圖。
和上面的ppt推算的是一致的。

這個作法就是Java的HashMap就是這麼實現的,簡單的解釋下,這個HashMap源碼的這個鏈表產生機制。
在put()方法裏面,最後部分有個以下的調用。
addEntry(hash, key, value, i);
解釋下幾個參數的意思:
1,hash:就是根據key算出來的一個值,源碼是這麼滴--int hash = hash(key);,
這個算出來的這個就至關因而身份證號碼,能夠惟一肯定一我的同樣,惟一肯定這個map
2,key:key就是咱們在往hashmap裏面put鍵值對的時候的key,使用map的時候,不是能夠根據key拿到value嗎。
3,value:這個同上啦,就是存的鍵值對的值。
4,i:源碼裏面是這麼滴--int i = indexFor(hash, table.length);實際意思就是這個鍵值對存放在底層數組的索引下標。
而後這個i,能夠對應到ppt上的那個取模以後的值,也就是肯定在數組上的下標。

雖然在put的時候,可能會出現擴容的問題,可是在這咱就不考慮這個,只考慮如何生成鏈表,以及鏈表上的鍵值對的順序。
createEntry(hash, key, value, bucketIndex);
這個方法就是真正的在建立一個節點到數組上。
這幾個參數是同樣的,和上面解釋的同樣的意思。

[java] view plain copy

  1. //先從數組上取下原來的值,給塞到新的節點去,而後把新的節點再放到數組上。  
  2. //也就是後來居上的道理。ppt上畫的也就有點毛病了。  
  3. //老師們嘛,就是 混口飯吃,通常都不斤斤計較這東西的。  
  4.    void createEntry(int hash, K key, V value, int bucketIndex) {  
  5.        Entry<K,V> e = table[bucketIndex];  
  6.        table[bucketIndex] = new Entry<>(hash, key, value, e);  
  7.        size++;  
  8.    }  

[java] view plain copy

  1.   static class Entry<K,V> implements Map.Entry<K,V> {  
  2.       final K key;  
  3.       V value;  
  4.       Entry<K,V> next;  
  5.       int hash;  
  6.       /** 
  7.        * Creates new entry. 
  8.        */  
  9.       Entry(int h, K k, V v, Entry<K,V> n) {  
  10.           value = v;  
  11.           next = n;  
  12.           key = k;  
  13.           hash = h;  
  14.       }  
  15. //******  


上面就是hashmap底層數組上存的元素的model。也是能造成鏈表的關鍵,有興趣的能夠看看1.7的hashmap的源碼。
 

 

三、4)再哈希、創建公共溢出區

3.再hash法,就是算hashcode的方法不止一個,一個要是算出來重複啦,再用另外一個算法去算。反正不少,直到不重複爲止咯。大師兄猜的

4.創建一個公共溢出區域,就是把衝突的都放在另外一個地方,不在表裏面。具體實現就 不知道啦,也是大師兄猜的。

總結一下的就是下面的四行字:

1.開放定址法(線性探測再散列,二次探測再散列,僞隨機探測再散列) 2.再哈希法 3.鏈地址法(Java hashmap就是這麼作的) 4.創建一個公共溢出區

相關文章
相關標籤/搜索