开放寻址法和链表法
开放寻址法
核心思想是,如果出现了散列冲突,我们就重新探测一个空闲位置,将其插入。
(1) 线性探测:我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。还记得我们刚讲的查找操作吗?在查找的时候,一旦我们通过线性探测方法,找到一个空闲位置,我们就可以认定散列表中不存在这个数据。但是,如果这个空闲位置是我们后来删除的,就会导致原来的查找算法失效。本来存在的数据,会被认定为不存在。这个问题如何解决呢?我们可以将删除的元素,特殊标记为 deleted。当线性探测查找的时候,遇到标记为 deleted 的空间,并不是停下来,而是继续往下探测
(2) 二次探测:探测步长不同,二次探测探测的步长就变成了原来的“二次方”,也就是说,它探测的下标序列就是 hash(key)+0,hash(key)+12,hash(key)+22
(3) 双重散列:有冲突就找第二个散列函数,再有再加
链表法
是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。在散列表中,每个“桶(bucket)”或者“槽(slot)”会对应一条链表,所有散列值相同的元素我们都放到相同槽位对应的链表中。
优缺点:
开放寻址法:都在数组里,序列化简单,冲突代价高,删除麻烦,装载因子不能太大,浪费内存空间
链表:内存利用率高,存储小对象的时候,你还要存储指针,比较消耗内存。我总结一下,基于链表的散列冲突处理方法比较适合存储大对象、大数据量的散列表,而且,比起开放寻址法,它更加灵活,支持更多的优化策略,比如用红黑树代替链表。