python数据结构与算法 29-2 冲突解决

冲突解决

现在返回到前面提到的冲突问题。当两个元素的哈希值指向同一个槽位,就应该有个系统的方法把第二个元素放进表中。这个过程叫做“冲突解决”。我们前面说过的,如果哈希函数是完美的,不会发生冲突。但完美无缺的事很少,所以冲突解决就成为哈希算法中的重要部分。

一种方法是为引起冲突的元素找到另一个位置。简单的做法就是从原来的位置开始,顺序向前查找,直到遇到一个空闲的槽位为止。注意的是我们可能需要循环地从第一个槽位开始以便覆盖整个哈希表。这种方法叫做开放地址法,因为它就是在哈希表中找下一个空闲槽位。通过系统地访问每个槽位,我们的开放地址技术叫做线性探测。

8显示了一个用余数法作哈希函数的整数列表(54,26,93,17,77,31,44,55,20)。表4显示了起始元素的哈希值。图5显示的是原来的内容,当我们试图放置440位的时候,冲突发生了,使用线性探测技术,我们按顺序一个槽位一个槽位地找,最终找到一个空闲槽位。这个例子中是1位。

再次,55也应该在0位上,但不得不放在2位上,因为这是第二个空闲位置。最后20的哈希值是9,但9已经被占了,不得不再作探测,我们查找了1001,和2位,最后在3位上发现一个空位。


一旦我们用开放地址和线性探测法建立了一个哈希表,那就有必要用同样的方法实现查找。假设我们要找93,计算哈希值,得到5,查找5号槽,果然发现93,这时可以返回True值。如果要找20呢?哈希值是9,但9位上坐着31,我们不能简单地返回False完事儿,因为我们知道可能存在冲突,这时就要进行一下顺序查找,从10位开始,逐个查找直到找到20或发现空槽位。

线性探测的一个不好的地方就是可能引起聚集现象,元素在表里成群出现。这是因为如果在同一槽位上发生很多冲突的话,周边的槽位就会被线性探测找到并填满,这会影响到其他插入的元素,就象前面我们看到插入20的情况,20不得不跨过0后面好几个元素才找到开放地址,如图9所示。


一种解决办法是延伸线性探测,改变就近查找空槽的方法,而是跳过几个槽位,这样冲突的元素可以分散开来,从而潜在减少了聚集现象的可能性。图10显示的是线性+3探测,意思是,冲突发生时,我们每隔3个位置探测一下是否空槽。

在发生冲突后查找另一个空槽的方法称为“再哈希”,用线性探测法,再哈希函数为

 newhashvalue=rehash(oldhashvalue) 

rehash(pos)=(pos+1)%sizeoftable.

+3探测法就要重定义为:

rehash(pos)=(pos+3)%sizeoftable

一般情况下:

rehash(pos)=(pos+skip)%sizeoftable.

Skip这个变理的选择必须保证表中所有的槽位都能访问到,否则,表中有些位置就会一直空闲。因此,为保证skip取值合适,一般建议表的大小是个质数,这也是为什么前面的例子中我们使用11

线性探测思想的一个变种称为二次探测。它不再使用一个常数的“跳步”,而是用再哈希函数来逐次提高哈希值如13579等。这就是说,如果第一次计算得到的哈希值是h,那么后续的值就是h+1h+4h+9h+16等,换句话说,二次探测使用了完全平方数作为跳步,图11所示为使用了这个技术的方法。


另一种处理冲突问题的方法是让槽位上保存一个指向链地址的引用,这个方法可以在一个槽位上存在很多元素。这样当发生冲突时,元素仍然可以放在这个槽位上。但是如果越来越多的元素放置在同一槽位上,查找的难度也要增加。图12显示了使用链地址方法解决冲突的方法。


当查找一个元素时,使用哈希函数计算出一个元素“应该”在的槽位值,既然每个槽位挂了一个集合,也要使用查找技术来判断是否存在。这样的好处是每个槽位上的元素数量要少很多,所以查找的效率更高。本周结尾我们将讨论哈希查找的效率。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值