要点一:哈希表长度 的确定是个两难的问题:太短则容易造成冲突(POJ->TLE);太长则浪费存储空间(POJ->MLE)。另外,有兴趣看一下“最小完美哈希函数”
要点二:哈希冲突 是必须解决的问题,主要有一下几种解决方案
(1)开放定址法 :当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探查到开放的 地址则表明表中无待查的关键字,即查找失败。
(2)链地址法 :将所有关键字相同的节点链接在同一个单链表中。
(3)再哈希法 :当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突
(4)建立一个公共溢出区 :假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。
其实这个东西很简单,主要要熟悉实现方法。下面直接上几个例题:
注:代码中尚未解决hash溢出问题!!!
POJ 2503 Babelfish ==> http://poj.org/problem?id=2503
C++代码(开放地址,线性探查 )
//poj 2503 Babelfish #include <iostream> //ELFhash函数是对字符串的散列 #include <string> using namespace std; #define M 1000000 //M如果较小则不能很好地避免冲突,解决冲突需要更多的时间,会TLE,比如200000 当然不能过大,会MLE,比如10000000 //M取适合的值,既可以避免hash开得太大,同时又可以减少冲突 string hash[M],res[M]; int ELFHash(string str) //ELFhash函数 { int h = 0; int x = 0; for(int i=0;i<str.size();++i) { h = (h << 4) + (str[i]); if ((x = h & 0xF0000000L) != 0) { h ^= (x >> 24); h &= ~x; } } return h % M; } int main() { string str,e,f,s; int ind; while(getline(cin,str)) //如果输入某行是“直接回车”的话,则str="",while("")导致退出循环 { if(str.size()==0) break; int pos=str.find(' '); e.assign(str,0,pos); //添加从下标[0]开始的pos个字符 f.assign(str,pos+1,str.size()-1-pos); ind=ELFHash(f); while(hash[ind]!="") ind=(ind+1)%M; hash[ind]=f; res[ind]=e; } while(cin>>s) { ind=ELFHash(s); if(hash[ind]=="") cout<<"eh\n"; else { while(hash[ind]!=s && hash[ind]!="") ind=(ind+1)%M; //开放地址法 线性探查法 if(res[ind]!=""){ cout<<res[ind]<<endl; }else{ cout<<"eh\n"; } } } return 0; }
C++代码: (链地址法 )
#include <iostream> #include <fstream> #include <string.h> #define N 100001 #define strSize 15 using namespace std; struct hash{ bool used; char fn[strSize],en[strSize]; hash* next; //用于冲突时构造链表 hash(){used=false; next=NULL;} hash(char *f,char *e) { strcpy(fn,f); strcpy(en,e); used=false; next=NULL; } }h[N]; int ELFhash(char *key){ unsigned long h=0; unsigned long x=0; while(*key) { h=(h<<4)+(*key++); //h左移4位,当前字符ASCII存入h的低四位 if( (x=h & 0xF0000000L)!=0) { //如果最高位不为0,则说明字符多余7个,如果不处理,再加第九个字符时,第一个字符会被移出 //因此要有如下处理 h^=(x>>24); //清空28~31位 h&=~x; } } return h % N; } int main() { freopen("acm.txt","r",stdin); char str[30],en[strSize],fn[strSize]; hash* p; int sign=1,key; while(gets(str)) { if(str[0]=='\0') { sign=0; continue; } if(sign) //输入字典 { sscanf(str,"%s %s",&en,&fn); key=ELFhash(fn); //获取hash值 if(!h[key].used) //对应到hash表中 { h[key].used=true; strcpy(h[key].en,en); strcpy(h[key].fn,fn); } else //处理冲突 { p=&h[key]; while(p->next != NULL) p=p->next; p->next=new hash(fn,en); } } else //输入外文 { key=ELFhash(str); if(!h[key].used) printf("eh\n"); else { p=&h[key]; while(p!=NULL) { if(!strcmp(str,p->fn)) { printf("%s\n",p->en); break; } else { p=p->next; } } if(p==NULL) printf("eh\n"); //不匹配的情况,不能少 } } } return 0; }