当将一个域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;
}
运行结果如下:
【注:若有错误,请指正~~~】