❶ 如何解決Hash中的沖突問題
1、開放定址法
用開放定址法解決沖突的做法是:當沖突發生時,使用某種探查(亦稱探測)技術在散列表中形成一個探查(測)序列。沿此序列逐個單元地查找,直到找到給定 的關鍵字,或者碰到一個開放的地址(即該地址單元為空)為止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。查找時探查到開放的 地址則表明表中無待查的關鍵字,即查找失敗。注意:
①用開放定址法建立散列表時,建表前須將表中所有單元(更嚴格地說,是指單元中存儲的關鍵字)置空。
②空單元的表示與具體的應用相關。
按照形成探查序列的方法不同,可將開放定址法區分為線性探查法、線性補償探測法、隨機探測等。
(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 。 2、拉鏈法
(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)拉鏈法的缺點
拉鏈法的缺點是:指針需要額外的空間,故當結點規模較小時,開放定址法較為節省空間,而若將節省的指針空間用來擴大散列表的規模,可使裝填因子變小,這又減少了開放定址法中的沖突,從而提高平均查找速度。
❷ 在線性表的散列儲存中,處理沖突的常用方法有哪兩種
線性表的散列存儲時中,處理沖突有
❸ 散列存儲方法的散列存儲中的沖突解決
映射函數可選擇的比較多,其實完全可以定義自己的映射函數,但是有時候為了降低沖突的概率設置了一些比較好的映射函數,比如求和取余,或者乘以一定的系數再求和取余等。
本文採用平方探測法解決了沖突問題,具體的實現如下所示:
1、結構體定義
#ifndef__HASHMAP_H_H_
#define__HASHMAP_H_H_
#includelist.h
#defineTABSIZE101
/*狀態變數*/
typedefenumSTATE{EMPTY=0,ACTIVE=1,DELETED=2}State;
/*鍵值結構體*/
typedefstruct_pair
{
char*key;
char*value;
}Pair_t,*Pair_handle_t;
/*每一個實際的存儲對象*/
typedefstruct_hashEntry
{
Pair_handle_tpair;
Statestate;
}HashEntry_t,*HashEntry_handle_t;
/*哈希表結構體,便於創建*/
typedefstruct_hashmap
{
HashEntry_t*map;
/*存儲實際的存儲量*/
intsize;
/*容量*/
intcapacity;
}Hashmap_t,*Hashmap_handle_t;
/*隱射函數類型定義*/
typedefint(*hashfunc)(constchar*,int);
#ifdef__cplusplus
externC
{
#endif
boolalloc_hashmap(Hashmap_handle_t*hashmap,intcapacity);
boolinit_hashmap(Hashmap_handle_thashmap,intcapacity);
boolinsert_hashnode(Hashmap_handle_thashmap,constchar*key,constchar*value);
Pair_handle_tsearch_hashnode(Hashmap_handle_thashmap,constchar*key);
char*GetValue(Hashmap_handle_thashmap,constchar*key);
booldelete_hashnode(Hashmap_handle_thashmap,constchar*key);
intLength(Hashmap_handle_thashmap);
intCapacity(Hashmap_handle_thashmap);
voiddelete_hashmap(Hashmap_handle_thashmap);
voidfree_hashmap(Hashmap_handle_t*hashmap);
char*key_pair(Pair_handle_tpair);
char*value_pair(Pair_handle_tpair);
Hashmap_handle_t_hashmap(Hashmap_handle_thashmap);
boolresize(Hashmap_handle_thashmap);
#ifdef__cplusplus
}
#endif
#endif
實現表的分配和創建,採用了動態分配的方式實現,這樣可能在性能上比不上靜態數據,但是為了實現數組大小的調整,我選擇了動態分配的實現方式。
/*分配一個新的對象,可以實現自動分配*/
boolalloc_hashmap(Hashmap_handle_t*hashmap,intcapacity)
{
HashEntry_handle_ttemp=NULL;
Hashmap_t*map=NULL;
if(*hashmap==NULL)
{
/*分配一個散列對象*/
map=(Hashmap_handle_t)malloc(sizeof(Hashmap_t));
if(map==NULL)
returnfalse;
/*指針指向當前對象*/
*hashmap=map;
map=NULL;
/*分配一個數組空間,大小可以控制*/
temp=(HashEntry_handle_t)malloc(
sizeof(HashEntry_t)*capacity);
if(temp!=NULL)
{
/*散列對象的指針指向數組*/
(*hashmap)->map=temp;
temp=NULL;
/*設置參數*/
(*hashmap)->capacity=capacity;
(*hashmap)->size=0;
/*初始化分配的數組空間*/
Tabinital((*hashmap)->map,capacity);
returntrue;
}
}
returnfalse;
}
/*初始化一個新的對象,這個對象已經創建,只是沒有初始化而已*/
boolinit_hashmap(Hashmap_handle_thashmap,intcapacity)
{
HashEntry_handle_ttemp=NULL;
if(hashmap!=NULL)
{
/*分配數組空間*/
temp=(HashEntry_handle_t)malloc(
sizeof(HashEntry_t)*capacity);
if(temp!=NULL)
{
/*完成對象的填充操作*/
hashmap->map=temp;
temp=NULL;
hashmap->capacity=capacity;
hashmap->size=0;
/*初始化數組對象*/
Tabinital(hashmap->map,capacity);
returntrue;
}
}
returnfalse;
}
關於數組中對象的創建,和釋放操作,如下所示:
/*分配一個pair對象*/
staticboolmake_pair(Pair_handle_t*pair,constchar*key,constchar*value)
{
Pair_handle_tnewpair=(Pair_handle_t)malloc(sizeof(Pair_t));
char*newstr=NULL;
if(newpair==NULL)
returnfalse;
newstr=(char*)malloc(strlen(key)+1);
if(newstr==NULL)
returnfalse;
strcpy(newstr,key);
newstr[strlen(key)]='