閉雜湊,開雜湊。散列表(雜湊表)(雜湊函式構造、處理衝突、查詢)
閉雜湊,開雜湊其實就是處理衝突的方式;
雜湊衝突的處理: 方法一: 閉雜湊(即開放地址法):當發生雜湊衝突時,如果該雜湊表還沒有被填滿,那麼就把該元素放到雜湊表的下一個空閒的位置。
優缺點下面介紹;
開雜湊法(雜湊桶):又名鏈地址法,先用雜湊函式計算每個資料的雜湊地址,把具有相同地址的元素歸於同一個集合之中,把該集合處理為一個連結串列,連結串列的頭節點儲存於雜湊表之中。 優缺點下面介紹;
雜湊之開雜湊和閉雜湊:
目的:實現一種結構,不經過任何比較,一次直接得到想要的元素。通過某種函式使元素的儲存位置與它的關鍵碼之間建立一種一一對映的關係。那麼就可以在查詢時快速的找到需要的元素。
雜湊概念
雜湊之雜湊方法:
插入元素時:根據需要插入元素的值,通過某種計算得出元素的儲存位置,將該元素插入到其對應的位置。 查詢元素時:根據需要查詢的元素進行某種計算得到其儲存位置,將該位置的元素與查詢的元素進行比較,若相同則查詢成功。 例如:資料集合為{180,750,460,430,800,600,541} 雜湊函式:Hash(key) = key%m;(m為記憶體單元的個數) 假設在該例子中,m為12; Hash(180) = 0; Hash(750) = 6; Hash(460) = 4; Hash(430) = 10; Hash(800) = 8; Hash(603) = 3; Hash(541) = 1; 所以這些資料集合在記憶體中的儲存為:
可是如果資料有衝突 了應該怎麼辦???
雜湊衝突
雜湊衝突:對於值不相同的元素但是卻有相同的雜湊值。 例如:對於兩個元素a,b並且a!=b 但是Hash(a) == Hash(b); 不同的元素通過相同的雜湊函式得到相同的雜湊地址。
引起雜湊衝突的原因:雜湊函式設計的不夠合理。 雜湊函式的設計原則:
如果雜湊表允許容納的元素個數為m,那麼元素的值域為0~m-1。 雜湊函式計算出來的地址儘量均勻的分佈整個空間之中。 常見的雜湊函式
直接定製法: 即取元素的某個線性函式為雜湊地址:Hash(key) = A*key +B; 例如:找出字串中只出現一次的字元。時間複雜度為O(N),空間複雜度為:O(1);(就可以使用該方法,開闢一個256個元素的陣列,進行統計每個元素出現的次數) 優點:簡單,均勻 適合於查詢比較小而且連續的情況。 除留取餘法:(比較常用的方法) Hash(key) = key % p;(p <= m && p 質數),m為散列表中允許的地址個數。 平方取中法: 對資料進行平方,然後取資料的中間3位為雜湊地址。 適合於:不知道資料的分佈情況,但是數字又不是很大的情況 若雜湊函式設計的非常合理,那麼產生雜湊衝突的概率就非常低,但是雜湊衝突是無法避免的。
雜湊衝突的處理: 方法一: 閉雜湊(即開放地址法):當發生雜湊衝突時,如果該雜湊表還沒有被填滿,那麼就把該元素放到雜湊表的下一個空閒的位置。 線性探測法查詢下一個位置: 例如:關鍵碼集合為:{37,25,14,36,49,57,11},設表的長度為12,Hash(key) = key%p(p = 11); Hash(37) = 4; Hash(25) = 3; Hash(14) = 3; Hash(36) = 3; Hash(49) = 5; Hash(57) = 2; Hash(11) = 0; 很明顯:這組資料的雜湊地址有衝突。 在插入時,如果該位置已經有元素了,就從該位置起向後找,找到一個空閒的位置就進行插入。 如下圖所示: 優點:簡單 易懂 缺點:一旦發生了雜湊衝突,所有的衝突連線在一起,很容易產生資料”堆積”。即不同的資料佔用可以利用的位置,就使得尋找其餘資料的位置需要進行多次比較,就會導致查詢的效率降低。
負載因子 散列表的負載因子的值為:α = 填入表中的元素個數 / 散列表的長度。 分析:由於表長是定值,那麼α就與”填入表中的元素個數”成正比。所以,α越大,就說明填入表中的元素個數越多,那麼產生衝突的可能性就越大;反之,α越小,就說明填入表中的越少,產生的衝突就越小,但是可能浪費的空間就越多。 對於開放地址法:負載因子特別重要,應該限制在07-0.8之內。若超過0.8,就可能產生衝突的概率非常大,那麼CPU快取不命中率也就越高。
二次探測法: 就是當有雜湊衝突時,尋找下一個空閒位置時,首先在該位置處加1的平方,若加1的平方的位置處依然有元素,那就加2的平方,知道找到一個空閒的位置為止。
方法2:開雜湊法(雜湊桶):又名鏈地址法,先用雜湊函式計算每個資料的雜湊地址,把具有相同地址的元素歸於同一個集合之中,把該集合處理為一個連結串列,連結串列的頭節點儲存於雜湊表之中。 例如:還是上面閉雜湊中的例子,當使用開雜湊的方法後,其每個元素的儲存為下圖所示:
由此可見:開雜湊法有效的解決了資料溢位,不過需要增設連結指標,增加了儲存的開銷。但是,總體而言,效率還是快的多。
理想的查詢是不經過任何比較就能根據所查關鍵嗎直接得到待查記錄所在的儲存位置。雜湊查詢技術就是朝該方向努力,它在關鍵碼和儲存位置之間建立一種對應關係,雜湊函式,由該函式可計算出關鍵碼唯一的地址。在儲存元素時通過計算通過計算關鍵碼的雜湊函式值確定儲存地址。查詢時,計算給定關鍵字的雜湊函式值得到儲存地址,直接查詢。
關鍵碼和儲存位置之間的對應函式是雜湊函式,也稱為雜湊函式,hash函式。
一、雜湊函式的構造方法
1、直接定址法
關鍵碼本身和地址之間存在某個線性函式關係時,雜湊函式取為關鍵碼的線性函式,即:H(key)=a*key+b,a、b均為常數。
這樣的雜湊函式優點就是簡單、均勻,也不會產生衝突,但問題是這需要事先知道關鍵字的分佈情況,適合査找表較小且連續的情況。由於這樣的限制,在現實應用中,直接定址法雖然簡單,但卻並不常用。
2、數字分析法
假設關鍵碼完全已知,且每個關鍵碼都是以某個數r為基數(例以10為基數的十進位制數)的值,則關鍵碼中若干位恰能構成分佈比較均勻的雜湊地址空間時,可取關鍵碼的若干位的組合作為雜湊地址。
3、除留餘數法
通過選擇適當的正整數p,按計算公式H(K)=Kmodp來計算關鍵碼K的雜湊地址。
若關鍵碼個數為n,散列表表長為m(一般m>=n),通常選p為小於或等於表長m的最大素數或不包含小於20的質因子的合數,一般也要求p>=n。
這種方法計算最簡單,也不需根據全部關鍵碼的分佈情況研究如何從中析取資料,最常用。
4、平方取中法
將關鍵碼K平方,取K^2中間幾位作為其雜湊地址H(K)的值。
假如有以下關鍵字序列{421,423,436},平方之後的結果為{177241,178929,190096},那麼可以取{72,89,00}作為Hash地址。
5、摺疊法
將關鍵碼從低位到高位(或從高位到低位)分割成位數相等的幾段,最後一段可以短些,然後將這些段構成的數值按照某種疊加方法求和。最後,在雜湊地址範圍限制下,取求和結果的最後幾位作為關鍵碼的雜湊函式值。
疊加方法:
(1)移位疊加:將各段數值最後一位對齊相加
(2)間界疊加:從各個數值段的一端到另一端來回摺疊後(奇數段位正序,偶數為倒序),以最後一位對齊後相加
例:
key=12360324711202065,雜湊表長度為1000,則應把關鍵字分成3位一段,在此捨去最低的兩位65,分別進行移位疊加和摺疊疊加,求得雜湊地址為105和907,如圖所示。
1 2 3 1 2 3
6 0 3 3 0 6
2 4 7 2 4 7
1 1 2 2 1 1
+) 0 2 0 +) 0 2 0
———————— —————
1 1 0 5 9 0 7
(a)移位疊加 (b) 間界疊加
6、隨機數法
採用隨機函式作為雜湊函式H(Key)=random(Key),其中random為隨機函式。
當關鍵碼長度不等時,採用該方法較恰當。
二、衝突的處理方法
1、開放定址法(建立閉散列表)
開放定址指散列表的地址對任何記錄資料都是開放的,即可儲存使用。但散列表長度一旦確定,總的可用地址是有限的。閉散列表表長不小於所需儲存的記錄數,發生衝突總能找到空的雜湊地址將其存入。查詢時,按照一種固定的順序檢索散列表中的相應項,直到找到一個關鍵字等於k或找到一個空單元將k插入,故是動態查詢結構。
1)線性探測法
從發生衝突位置的下一個位置開始尋找空的雜湊地址。發生衝突時,線性探測下一個雜湊地址是:Hi=(H(key)+di)%m,(di=1,2,3...,m-1)。閉散列表長度為m。它實際是按照H(key)+1,H(key)+2,...,m-1,0,1,H(key)-1的順序探測,一旦探測到空的雜湊地址,就將關鍵碼記錄存入。
該方法會產生堆積現象,即使是同義詞也可能會爭奪同一個地址空間,今後在其上的查詢效率會降低。
2)二次探測法
發生衝突時,下一位置的探測採用公式:Hi=(H(key)+di)%m,(di=1^2,-1^2,2^2,-2^2,.....,q^2,-q^2,q<=根號下m)
在一定程度上可解決線性探測中的堆積現象。
3)隨機探測法
di為{1,2,3,...,m-1}中的數構成的一個隨機數列中順序取的一個數
4)再雜湊函式法
除基本雜湊函式外,事先設計一個雜湊函式序列,RH1,RH2,...,RHk,k為某個正整數。RHi均為不同的雜湊函式。對任一關鍵碼,若在某一雜湊函式上發生衝突,則再用下一個雜湊函式,直到不發生衝突為止。
5)建立公共溢位區(單鏈表或順序表實現)
另外開闢一個儲存空間,當發生衝突時,把同義詞均順序放入該空間。若把散列表看成主表或父表,則公共的同義詞表就是一個次表或子表。查詢時,現在散列表中查,找不到時再去公共同義詞子表順序查詢。
2、拉鍊法(鏈地址法、建立開散列表)
將所有雜湊地址相同的記錄儲存在同一個單鏈表中,該單鏈表為同義詞單鏈表,或同義詞子表。該單鏈表頭指標儲存在散列表中。散列表就是個指標陣列,下標就是由關鍵碼用雜湊函式計算出的雜湊地址。初始,指標陣列每個元素為空指標,相當於所有單鏈表頭指標為空,以後每掃描到一條記錄,按其關鍵碼的雜湊地址,在相應的單鏈表中加入含該記錄的節點。開散列表容量可很大,僅受記憶體容量的限制。
例:具體的關鍵字列表為(19,14,23,01,68,20,84,27,55,11,10,79),則雜湊函式為H(key)=key MOD 13。則採用除留餘數法和鏈地址法後得到的預想結果應該為:
三、散列表上的查詢
1、時間複雜度分析
查詢成功時的平均查詢長度ASLsucc、查詢不成功時的平均查詢長度ASLunsucc
1)具體閉散列表的查詢效率分析
關鍵碼{19,14,23,1,68,20,84,27,55,11,10,79},散列表長度m=16,除留餘數法設計雜湊函式,H(key)=key%13,
(1)線性探測法處理衝突
只考慮關鍵碼的比較,不考慮判空等操作,以下列出比較次數
雜湊地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 關鍵碼 14 1 68 27 55 19 20 84 79 23 11 10 成功時 1 2 1 4 3 1 1 3 9 1 1 3 不成功時 12 11 10 9 8 7 6 5 4 3 2 1 ASLsucc=(1*6+2*1+3*4+4*1+9*1)/n=30/12=2.5 ASLunsucc=(1+2+...+12)/m=78/16=4.375
在ASLunsucc的計算中,假設每個地址被查到的概率相等(等於1/m=1/16),
(2)二次探測法
雜湊地址 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 關鍵碼 27 14 1 68 55 84 19 20 10 23 11 79 成功時 3 1 2 1 2 3 1 1 3 1 1 5 不成功時 2 6 4 4 3 5 5 1 2 3 1 1 1
ASLsucc=(1*6+2*2+3*3+5*1)/n=24/12=2
ASLunsucc=(1*3+2*2+3*2+4*2+5*2+6*1)/m=37/16=2.3125
2)具體開散列表的查詢效率分析
如上圖拉鍊法處理衝突中的雜湊表,
ASLsucc=(1*6+2*4+3*1+4*1)/n=21/12=1.75
ASLunsucc=(1*2+2*3+4*1)/m=12/16=0.75
一般情況下,處理衝突方法相同的散列表,其查詢成功時的平均查詢長度依賴於散列表的裝填因子:a=表中填入的記錄數/雜湊表長度
a越小,發生衝突的可能性越小,反之,a越大,表中已填入記錄越多,再填記錄時,發生衝突的可能性越大,查詢時給定值需與之進行比較關鍵碼數目越多。