當將一個域U中的元素映射到一個哈希表T中時,我們如何映射?若映射到同一位置怎麼辦?前一個問題我們通過hash函數來解決,後一個問題我們通過“衝突處理”解決。
1.Hash 函數
(1)除法
h(k) = k mod m
m的選取:素數,且不要太靠近 2的冪次方
(2)乘法
其中,0<A<1(Knuth建議A=0.6180339887), m = 2^r(對m選取沒有特別要求,一般選爲2的冪次方), w表示計算機的字長(如32)。
EG:當m=8=2^3,w=7時:
(3)全域哈希
上面兩種方法通常仍會讓不同元素映射到同一槽(slot)中,可以通過構造全域哈希集,隨機選擇hash函數進行映射。構造全域Hash集的方法很多,下面僅介紹其中一種:
其中,a={0,1,...,p-1},b={1,2,...,p-1},p>m,p 足夠大
這一類哈希函數組成全域哈希集:
如果h隨機從H中選取,那麼元素x和y衝突的概率爲1/m.
2.衝突處理
(1)鏈表法
平均搜索時間爲Θ(1+α)。當n=O(m),α=O(1)
(2)開放地址法
線性探測,平方探測
開放地址法一次不成功搜索的探測期望爲:1/(1-α);一次成功搜索的探測期望爲:(1/α)lg(1/(1-α))。
3.完美哈希(Perfect Hashing)
當給定n個key值,構建一個大小爲m=O(n)的靜態hash表,使得最壞情況下的搜索時間爲O(1)。爲解決這個問題,需要使用2級結構,每級使用全域哈希。
EG: K=<10,22,37,40,52,60,70,72,75>,第一級hash函數爲:h(k)=((ak+b) mod p) mod m, 其中a=3,b=42,p=101,m=9.第二級哈希表S(j) 存儲所有映射到槽j的key.其中S(j)的大小滿足:m(j)=n(j)²,同樣利用h(k)=((ak+b) mod p) mod m求解,但需要注意a,b,p,m與第一級的不同,並且第二級中的每個S(j)的a,b,p,m也不同。
衝突分析:
如果利用從一個全域hash集中隨機選擇的hash函數h,將n個key存儲在一個大小爲m=n²的hash表中,那麼出現碰撞的概率小於1/2
存儲分析:
對於第一級選擇m=n,第二級的大小滿足:m(i)=n(i)²,其中n(i)表示映射到槽i中的所有key,那麼:
三種hash映射處理完整代碼如下(鏈表處理元素衝突):
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<string>
using namespace std;
struct TNode{
int key;
TNode *next;
};
void Print(TNode *T,int m)
{//Print Hash Tables
TNode *p;
for(int i=0;i<m;i++)
{
p=T[i].next;
cout<<"T["<<i<<"] -->";
while(p!=NULL)
{
cout<<p->key<<" ";
p=p->next;
}
cout<<endl;
}
}
void Init_HashTables(TNode *T,int m)
{//init hash tables
//T=new TNode[m];
for(int i=0;i<m;i++)
{
T[i].key = i;
T[i].next=NULL;
}
}
int H1(int k,int m)
{//division
return k % m;
}
int H2(int k,int m)
{//multiplication
double A=0.6180339887;
double dk=(double)k;
double res=((A*dk)-(int)(A*dk))*m;
return (int)res;
}
int H3(int k,int m)
{//universal hash
srand((unsigned)time(NULL));
int p=701;
int a=rand()%(p-1)+1; //[1...p-1]
int b=rand()%p; //[0...p-1]
int res=((a*k+b)%p)%m;
return res;
}
void InsertToHTable(TNode *T,int hkey,int k)
{
TNode *s,*p;
s=new TNode();
s->key=k;
s->next=NULL;
p=&T[hkey];
while(p->next!=NULL)
p=p->next;
p->next=s;
}
void Create_HashTables(TNode *T,int K[],int m,int n,string model)
{
int hkey;
Init_HashTables(T,m); //init the hash table
for(int i=0;i<n;i++)
{
if(model=="div")
hkey=H1(K[i],m);
if(model=="mul")
hkey=H2(K[i],m);
if(model=="uni")
hkey=H3(K[i],m);
InsertToHTable(T,hkey,K[i]);
}
}
int main()
{
int K[]={10,22,37,40,52,60,70,72,75,32,48,21,8,15};
int n=sizeof(K)/sizeof(int);
int m=7;
//TNode *T1;
TNode *T1=new TNode[m];
TNode *T2=new TNode[m];
TNode *T3=new TNode[m];
cout<<"Create hash tables in model of division:"<<endl;
Create_HashTables(T1,K,m,n,"div"); //in inserting method
Print(T1,m);
cout<<"Create hash tables in model of multipulication:"<<endl;
Create_HashTables(T2,K,m,n,"mul");
Print(T2,m);
cout<<"Create hash tables in model of univeral:"<<endl;
Create_HashTables(T3,K,m,n,"uni");
Print(T3,m);
return 0;
}
運行結果如下:
【注:若有錯誤,請指正~~~】