看了ConcurrentHashMap的實現, 使用的是拉鍊法.
算法
雖然咱們不但願發生衝突,但實際上發生衝突的可能性還是存在的。當關鍵字值域遠大於哈希表的長度,並且事先並不知道關鍵字的具體取值時。衝突就不免會發 生。另外,當關鍵字的實際取值大於哈希表的長度時,並且表中已裝滿了記錄,若是插入一個新記錄,不只發生衝突,並且還會發生溢出。所以,處理衝突和溢出是 哈希技術中的兩個重要問題。
一、開放定址法
用開放定址法解決衝突的作法是:當衝突發生時,使用某種探查(亦稱探測)技術在散列表中造成一個探查(測)序列。沿此序列逐個單元地查找,直到找到給定 的關鍵字,或者碰到一個開放的地址(即該地址單元爲空)爲止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。查找時探查到開放的 地址則代表表中無待查的關鍵字,即查找失敗。
注意:
①用開放定址法創建散列表時,建表前須將表中全部單元(更嚴格地說,是指單元中存儲的關鍵字)置空。
②空單元的表示與具體的應用相關。
按照造成探查序列的方法不一樣,可將開放定址法區分爲線性探查法、線性補償探測法、隨機探測等。
(1)線性探查法(Linear Probing)
該方法的基本思想是:
將散列表T[0..m-1]當作是一個循環向量,若初始探查的地址爲d(即h(key)=d),則最長的探查序列爲:
d,d+l,d+2,…,m-1,0,1,…,d-1
即:探查時從地址d開始,首先探查T[d],而後依次探查T[d+1],…,直到T[m-1],此後又循環到T[0],T[1],…,直到探查到T[d-1]爲止。
探查過程終止於三種狀況:
(1)若當前探查的單元爲空,則表示查找失敗(如果插入則將key寫入其中);
(2)若當前探查的單元中含有key,則查找成功,但對於插入意味着失敗;
(3)若探查到T[d-1]時仍未發現空單元也未找到key,則不管是查找仍是插入均意味着失敗(此時表滿)。
利用開放地址法的通常形式,線性探查法的探查序列爲:
hi=(h(key)+i)%m 0≤i≤m-1 //即di=i
用線性探測法處理衝突,思路清晰,算法簡單,但存在下列缺點:
① 處理溢出需另編程序。通常可另外設立一個溢出表,專門用來存放上述哈希表中放不下的記錄。此溢出表最簡單的結構是順序表,查找方法可用順序查找。
② 按上述算法創建起來的哈希表,刪除工做很是困難。假如要從哈希表 HT 中刪除一個記錄,按理應將這個記錄所在位置置爲空,但咱們不能這樣作,而只能標上已被刪除的標記,不然,將會影響之後的查找。
③ 線性探測法很容易產生堆聚現象。所謂堆聚現象,就是存入哈希表的記錄在表中連成一片。按照線性探測法處理衝突,若是生成哈希地址的連續序列愈長 ( 即不一樣關鍵字值的哈希地址相鄰在一塊兒愈長 ) ,則當新的記錄加入該表時,與這個序列發生衝突的可能性愈大。所以,哈希地址的較長連續序列比較短連續序列生長得快,這就意味着,一旦出現堆聚 ( 伴隨着衝突 ) ,就將引發進一步的堆聚。
(2)線性補償探測法
線性補償探測法的基本思想是:
將線性探測的步長從 1 改成 Q ,即將上述算法中的 j = (j + 1) % m 改成: j = (j + Q) % m ,並且要求 Q 與 m 是互質的,以便能探測到哈希表中的全部單元。
【例】 PDP-11 小型計算機中的彙編程序所用的符合表,就採用此方法來解決衝突,所用表長 m = 1321 ,選用 Q = 25 。
(3)隨機探測
隨機探測的基本思想是:
將線性探測的步長從常數改成隨機數,即令: j = (j + RN) % m ,其中 RN 是一個隨機數。在實際程序中應預先用隨機數發生器產生一個隨機序列,將此序列做爲依次探測的步長。這樣就能使不一樣的關鍵字具備不一樣的探測次序,從而能夠避 免或減小堆聚。基於與線性探測法相同的理由,在線性補償探測法和隨機探測法中,刪除一個記錄後也要打上刪除標記。
二、拉鍊法
(1)拉鍊法解決衝突的方法
拉鍊法解決衝突的作法是:將全部關鍵字爲同義詞的結點連接在同一個單鏈表中。若選定的散列表長度爲m,則可將散列表定義爲一個由m個頭指針組成的指針數 組T[0..m-1]。凡是散列地址爲i的結點,均插入到以T[i]爲頭指針的單鏈表中。T中各份量的初值均應爲空指針。在拉鍊法中,裝填因子α能夠大於 1,但通常均取α≤1。
【例】設有 m = 5 , H(K) = K mod 5 ,關鍵字值序例 5 , 21 , 17 , 9 , 15 , 36 , 41 , 24 ,按外鏈地址法所創建的哈希表以下圖所示:編程
(2)拉鍊法的優勢
與開放定址法相比,拉鍊法有以下幾個優勢:
①拉鍊法處理衝突簡單,且無堆積現象,即非同義詞決不會發生衝突,所以平均查找長度較短;
②因爲拉鍊法中各鏈表上的結點空間是動態申請的,故它更適合於造表前沒法肯定表長的狀況;
③開放定址法爲減小衝突,要求裝填因子α較小,故當結點規模較大時會浪費不少空間。而拉鍊法中可取α≥1,且結點較大時,拉鍊法中增長的指針域可忽略不計,所以節省空間;
④在用拉鍊法構造的散列表中,刪除結點的操做易於實現。只要簡單地刪去鏈表上相應的結點便可。而對開放地址法構造的散列表,刪除結點不能簡單地將被刪結 點的空間置爲空,不然將截斷在它以後填人散列表的同義詞結點的查找路徑。這是由於各類開放地址法中,空地址單元(即開放地址)都是查找失敗的條件。所以在 用開放地址法處理衝突的散列表上執行刪除操做,只能在被刪結點上作刪除標記,而不能真正刪除結點。
(3)拉鍊法的缺點
拉鍊法的缺點是:指針須要額外的空間,故當結點規模較小時,開放定址法較爲節省空間,而若將節省的指針空間用來擴大散列表的規模,可以使裝填因子變小,這又減小了開放定址法中的衝突,從而提升平均查找速度。spa
本文同步分享在 博客「xiangzhihong8」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。.net