哈希(散列)查找

 

基本思想:在记录的存储地址和它的关键字之间建立一个确定的对应关系;这样,不经过比较,一次存取就能得到所查元素的查找方法。

哈希函数通常是一种压缩映象,所以冲突不可避免,只能尽量减少;当冲突发生时,应该有处理冲突的方法。设计一个散列表应包括:

①  散列表的空间范围,即确定散列函数的值域;

②  构造合适的散列函数,使得对于所有可能的元素(记录的关键字),函数值均在散列表的地址空间范围内,且出现冲突的可能尽量小;

③  处理冲突的方法。即当冲突出现时如何解决。

 

哈希函数的构造

1 直接定址法

取关键字或关键字的某个线性函数作哈希地址,即H(key)=key或H(key)=a·key+b(a,b为常数)

特点:直接定址法所得地址集合与关键字集合大小相等,不会发生冲突,但实际中很少使用。

 

2 数字分析法

对关键字进行分析,取关键字的若干位或组合作为哈希地址。

适用于关键字位数比哈希地址位数大,且可能出现的关键字事先知道的情况。

 

3 平方取中法

将关键字平方后取中间几位作为哈希地址。

一个数平方后中间几位和数的每一位都有关,则由随机分布的关键字得到的散列地址也是随机的。散列函数所取的位数由散列表的长度决定。这种方法适于不知道全部关键字情况,是一种较为常用的方法。

 

4 折叠法

将关键字分割成位数相同的几部分(最后一部分可以不同),然后取这几部分的叠加和作为哈希地址。

数位叠加有移位叠加和间界叠加两种。

◆ 移位叠加:将分割后的几部分低位对齐相加。

◆ 间界叠加:从一端到另一端沿分割界来回折迭,然后对齐相加。

       适于关键字位数很多,且每一位上数字分布大致均匀情况。

 

5  除留余数法

取关键字被某个不大于哈希表表长m的数p除后所得余数作哈希地址,即H(key)=key MOD p (p£m)是一种简单、常用的哈希函数构造方法。

利用这种方法的关键是p的选取,p选的不好,容易产生同义词。p的选取的分析:

◆ 选取p=2i(p£m):运算便于用移位来实现,但等于将关键字的高位忽略而仅留下低位二进制数。高位不同而低位相同的关键字是同义词。

◆ 选取p=q´f(q、f都是质因数,p£m):则所有含有q或f因子的关键字的散列地址均是q或f的倍数。

◆ 选取p为素数或p=q´f(q、f是质数且均大于20,p£m):常用的选取方法,能减少冲突出现的可能性。

 

6  随机数法

取关键字的随机函数值作哈希地址,即H(key)=random(key)

当散列表中关键字长度不等时,该方法比较合适。

选取哈希函数,考虑以下因素

◆ 计算哈希函数所需时间;

◆ 关键字的长度;

◆ 哈希表长度(哈希地址范围);

◆ 关键字分布情况;

◆ 记录的查找频率。

 

冲突处理的方法

1  开放定址法

基本方法:当冲突发生时,形成某个探测序列;按此序列逐个探测散列表中的其他地址,直到找到给定的关键字或一个空地址(开放的地址)为止,将发生冲突的记录放到该地址中。

散列地址的计算公式是:

Hi(key)=(H(key)+di)  MOD m,i=1, 2, …, k(k£m-1)

其中:H(key):哈希函数;m:散列表长度;

di:第i次探测时的增量序列;

Hi(key) :经第i次探测后得到的散列地址。

⑴  线性探测法

将散列表T[0 …m-1]看成循环向量。当发生冲突时,从初次发生冲突的位置依次向后探测其他的地址。

增量序列为:di=1, 2, 3, …, m-1

设初次发生冲突的地址是h,则依次探测T[h+1],T[h+2]…,直到T[m-1]时又循环到表头,再次探测T[0],T[1]…,直到T[h-1]。探测过程终止的情况是:

◆ 探测到的地址为空:表中没有记录。若是查找则失败;若是插入则将记录写入到该地址;

◆ 探测到的地址有给定的关键字:若是查找则成功;若是插入则失败;

◆ 直到T[h]:仍未探测到空地址或给定的关键字,散列表满。

线性探测法的特点

◆ 优点:只要散列表未满,总能找到一个不冲突的散列地址;

◆ 缺点:每个产生冲突的记录被散列到离冲突最近的空地址上,从而又增加了更多的冲突机会(这种现象称为冲突的“聚集”)。

 

⑵  二次探测法

增量序列为:di=1²,-1²,2²,-2²,3²,……±k²  (k£⌊m/2⌋)

上述例题若采用二次探测法进行冲突处理,则:

H(15)=15  MOD 7=1         H(14)=14  MOD 7=0

H(28)=28  MOD 7=0     冲突      H1(28)=1     又冲突

H2(28)=4

H(26)=26  MOD 7=5

H(56)=56  MOD 7=0     冲突      H1(56)=1     又冲突

H2(56)=0   又冲突    H3(56)=4    又冲突      H4(56)=2

H(23)=23  MOD 7=2      冲突      H1(23)=3

二次探测法的特点

◆ 优点:探测序列跳跃式地散列到整个表中,不易产生冲突的“聚集”现象;

◆ 缺点:不能保证探测到散列表的所有地址。

 

⑶  伪随机探测法

增量序列使用一个伪随机函数来产生一个落在闭区间[1,m-1]的随机序列。

 

2  再哈希法

构造若干个哈希函数,当发生冲突时,利用不同的哈希函数再计算下一个新哈希地址,直到不发生冲突为止。即:Hi=RHi(key)     i=1, 2, …, k

RHi :一组不同的哈希函数。第一次发生冲突时,用RH1计算,第二次发生冲突时,用RH2计算…依此类推知道得到某个Hi不再冲突为止。

◆  优点:不易产生冲突的“聚集”现象;

◆  缺点:计算时间增加。

 

3  链地址法

方法:将所有关键字为同义词(散列地址相同)的记录存储在一个单链表中,并用一维数组存放链表的头指针。

设散列表长为m,定义一个一维指针数组:

RecNode *linkhash[m],其中RecNode是结点类型,每个分量的初值为空。凡散列地址为k的记录都插入到以linkhash[k]为头指针的链表中,插入位置可以在表头或表尾或按关键字排序插入。

 

4  建立公共溢出区

方法:在基本散列表之外,另外设立一个溢出表保存与基本表中记录冲突的所有记录。

设散列表长为m,设立基本散列表hashtable[m],每个分量保存一个记录;溢出表overtable[m],一旦某个记录的散列地址发生冲突,都填入溢出表中。

 

哈希查找过程及分析

 

查找算法

#define NULLKEY   -1    /*   根据关键字类型定义空标识   */

typedef  struct

         KeyType   key ;     /*   关键字域   */

         otherType  otherinfo ;   /*   记录的其它域   */

}RecType ;

 

int  Hash_search(RecType HT[], KeyType k, int m)

/*    查找散列表HT中的关键字K,用开放定址法解决冲突   */

         int h, j ;

         h=h(k) ;

         while (j<m && !EQ(HT[h].key, NULLKEY) )

         {  

                   if (EQ(HT[h].key, k) )   return(h) ;

                   else h=R(k, ++j) ;  

         }

         return(-1) ;

}

 

#define M 15

typedef struct node

         KeyType key;

         struct node *link;

}HNode;

 

HNode *hash_search(HNode *t[], KeyType k)

         HNode *p;   

         int i;

         i=h(k);

         if (t[i]==NULL)     return(NULL);

         p=t[i];

         while(p!=NULL)

                   if (EQ(p->key, k))  return(p); 

                   else  p=p->link;

         return(NULL);

}      /*   查找散列表HT中的关键字K,用链地址法解决冲突  */

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值