查找
哈希表的查找过程和造表过程基本一致:根据给定的关键字key,用该表对应的哈希函数求得哈希地址,判断该地址的记录与key是否相同。若不相同,则用此哈希表处理冲突的方法来找到下一个哈希地址,直到哈希地的记录为空或者找到与key值相同的记录为止。
以开放定址等方法处理冲突的哈希表的查找算法
//开放定址哈希表的存储结构
int hashsize[]={997,...}; //哈希表容量递增表,素数序列
typedef struct Elemtype //数据元素类型定义
{
KeyType key; //关键字
int a; //其他数据
int b;
};
typedef struct
{
Elemtype *elem; //数据元素存储基址,动态分配数组
int count; //当前数据元素个数
int sizeindex; //hashsize[sizeindex]为当前表的容量
}HashTable;
#define SUCCESS 1;
#define UNSUCCESS 0;
#define DUPLICATE -1;
#define NULLKEY 0; //定义key为0时为空
void collision(int &p,int &c)
//开放定址法求下一个哈希地址,以线性探测再散列为例
{
p=(++p) % 13; //处理冲突方法
}
int EQ(KeyType k1,KeyType k2) //判等函数
{
if(k1==k2)
return 1;
else return 0;
}
Status SearchHash(HashTable h,KeyType k,int &p,int &c){
/*在开放定址哈希表h中查找关键字为k的元素,若查找成功,,以p指示待查数据在表中的位置,
并返回success;否则,以p指示插入位置,并返回unsuccess;c用来记录冲突次数,供建表时参考
*/
p=Hash(k); //求k的哈希地址
while(h.elem[p].key!=NULLKEY && !EQ(k,h.elem[p].key))
//哈希地址p有记录且k与该地址的key不相等
collision(p,++c); //求下一个哈希地址,比如此处用开放定址法
if(EQ(k,h.elem[p].key)) //k与哈希地址p的key相等,查找成功
return SUCCESS;
else return UNSUCCESS; //查找不成功,此时地址p的记录为空,作为插入位置
}//Searchhash
开放定址哈希表的插入操作
Status InsertHash(HashTable &h,Elemtype e)
//查找不成功时插入元素e到哈希表h中,并返回;若冲突次数过多,则重新建表
{
if(SearchHash( h, e.key, p, c) ) //哈希表中已有该元素,无需插入
return DUPLICATE;
else if(c < hashsize[h.sizeindex]/2) //冲突次数c未达到上限,c的阈值可调
{
h.elem[p]=e; //插入元素e
++h.count; //元素个数增加
return;
}
else {
RecreateHashTable(h); //重新建表
return SUCCESS;
}
}
分析
虽然哈希表在关键字和记录位置之间建立了直接映像,但由于冲突的产生,使得哈希表查找过程仍是一个给定制和关键字进行比较的过程。因此,仍需要以 平均查找长度 作为衡量哈希表查找效率的量度。
一般来说,线性探测再散列在处理冲突过程中易产生记录的二次聚集,使得哈希函数不相同的记录产生新的冲突;而链地址法处理冲突不会发生类似的情况,因为哈希地址记录在不同的链表中。而在处理冲突方法相同的哈希表中,其平均查找长度依赖于哈希表的 装填因子 ,其定义为
装填因子 = 表中填入的记录数 / 哈希表长度
装填因子表示哈希表的装满程度。可以直观看出,装填因子越小,发生冲突的可能性越小。