选择一个“好”的散列函数可以在在一定程度上减少冲突,但在实际应用中很难完全避免冲突,所以选择一个有效的处理冲突的方法是非常重要的。创建散列表和查找散列表都会遇到冲突,这两种情况下处理冲突的方法应该是一致的。
创建散列表的方法和散列表本身的组织形式有关。按照组织形式的不同通常分位两大类:开放地址法和链地址法。
1.开放地址法
开放地址法的基本思想时:将记录都存储在散列表数组中,当某一记录关键字经过散列函数的计算所得到的初始地址H0此时已有其他关键字占据(即发生冲突),采取合适的方法计算得到另一个地址,如果仍然发生冲突,就以此地址为基础采取相同的计算方法,知道所得到的地址不再发生冲突。
这种方法在寻找下一个地址时,原来的数组空间对于所有的元素都是开放的,不管关键字key经过哈希函数计算所得到的初始地址是否与之相同,也没有其他的要求任何的元素中可以存储,所以称作开放地址法。
可用公式表示为
其中H(key)表示哈希函数,m为散列表长度,di为增量序列。
根据di取值的不同可以分为以下两种情况
(1)线性探测法
这种探测方法可以将散列表假象成一个循环表(实际并不是),发生冲突时,从冲突的下一单元顺序寻找空单元,如果最后一个位置都没有找到空单元,则回到表头继续查找,直到找到一个空位,就把此单元放入此空位。如果直到发生冲突的第一个单元(即经过哈希函数所得到的第一个初始地址)都没空位,则说明散列表已满,需要进行溢出处理
将一组关键字(9,1,23,14,55,20,84,27),采用散列函数:H(key)=key%7,表长为10,用线性探测法处理冲突
9%7=2 1%7=1 23%7=2(与9发生冲突)H=(H0+di)%(10-1) di=1 14%7=0
55%7=6 20%7=6(与55发生冲突)H=(H0+di)%(10-1) di=1
84%7=0(与14发生冲突) di=1,与1发生冲突 di=2,与9发生冲突 di=3,与23发生冲突 di=4,无冲突,所以di=4
27%7=6,与55,20发生冲突,所以di=2
(2)二次探测法
将一组关键字(9,1,23,14,55,20,84,27),采用散列函数:H(key)=key%7,表长为10,用二次探测法处理冲突
注意不是每次加di,而是从每加一次di都是从初始位置开始加的
2.链地址法
链地址法的基本思想是:将具有相同散列地址的记录放在同一个单链表中,称为同义词链表,不需要处理冲突。
以前面相同的题为例,采用尾插法,下面是示意图
以下是代码演示
线性探测法的代码实现
链式探测法的代码实现