散列表处理冲突的方法

一、开放地址法

所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。

1.1 线性探测法

fi ( key ) = ( f ( key ) + di ) MOD m (di=1,2,3,4,…,m-1)

会出现不是同义词却需要争夺一个地址的情况,我们称这种情况为堆积。

1.2 二次探测法

关键字集合『12,67,56,16,25,37,22,29,15,47,48,34』

下标 0 1 2 3 4 5 6 7 8 9 10 11
关键字 12 25 37 15 16 29 48 67 56 22 47

当最后一个key=34,f(key)=10,与22的位置冲突。可是22后面没有空位了,虽然可以不断求余数后得到结果,但是效率太低了。
使用12,-12,22,-22,…,q2,-q2,q<=m/2就可以双向寻找可能的空位子,增加平方运算时为了不让关键字都聚集在某一块区域。称之为二次探测法。

fi ( key ) = ( f ( key ) + di ) MOD m (di=12,-12,22,-22,…,q2,-q2,q<=m/2)

1.3 随机探测法

在冲突时,对于位移量di采用随机函数计算得到,我们称之为随机函数。

这里的随机其实是伪随机数。伪随机是说如果我们设置的随机种子相同,则不断调用随机函数可以生成一个不会重复的数列。我们在查找时候,用相同的随机种子,它每次得到的数列都是相同的,相同的di当然可以得到相同的散列地址。

fi ( key ) = ( f ( key ) + di ) MOD m (di是一个随机数列)


二、再散列函数法

对于我们的散列函数来说,我们可以事先准备多个散列函数。

fi ( key ) = RHi ( key ) (i=1,2,…,k)

RHi 就是不同的散列函数,可以将前面说散列表查找及其函数的除留余数、折叠、平方取中全部用上。每当出现散列地址冲突,就换一个散列函数计算。这种方法能够使得关键字不产生聚集,但是相应地会增加计算的时间。


三、链地址法

思路换一下,为什么有冲突就要换地方呢?我们直接在原地不行吗?于是有了链地址法。

将所有关键字为同义词的记录存储在一个单链表中,我们称之为同义词子表,在散列表中只存储所有同义词子表的头指针。

对于关键字集合{12,67,56,16,25,37,22,9,15,47,48,34},我们用前面的12作为除数,进行除留余数法,可以得下如下结构:
散列表处理冲突的方法_第1张图片
链地址法对于可能造成很多冲突的散列函数来说,提供了绝不会出现找不到地址的保证。当然,这也就带来了查找时需要遍历单链表的性能损耗。


四、公共溢出区法

这个方法更好理解,当发生冲突的时候,凡是冲突的跟我走,给这些冲突找个地儿呆着。我们为所有冲突的关键字建立一个公共的溢出区来存放。

就前面的例子而言,我们共有三个关键字{37,48,34}与之前的关键字有冲突,那么将它们存储在溢出表中。
散列表处理冲突的方法_第2张图片在查找的时候,对于给定值通过散列函数计算出散列地址之后,先与基本表对应位置进行比对,如果相等,则查找成功;如果不相等,则到溢出表进行顺序查找。如果相对于基本表而言,有冲突的数据很少的情况下,公共溢出区的结构对查找性能来说还是非常高的。

你可能感兴趣的:(数据结构与算法)