算法導論 第十一章:哈希表

           當將一個域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;
	}
運行結果如下:




【注:若有錯誤,請指正~~~】



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章