哈希表是一种非常重要的数据结构,甚至可以说是查询方面最快的数据结构了!
与其他数据结构相比,哈希表不需要比较查找,而是通过某种哈希函数直接找到目标位置。
向哈希表中插入元素,只需得到该元素的关键码,以此计算元素的存储位置进行存放。
在哈希表中搜索仍然进行同样的计算,把求得的存储位置中的元素与需要查找的元素进行比较,如果相等则查找成功。
然而天下没有免费的午餐,虽然哈希结构的搜索速度极快,但是一旦谈到哈希表,就一定要面对哈希冲突,再精妙绝伦的哈希函数,也有一定概率会发生哈希冲突。
那么什么是哈希冲突呢?即通过不同的关键字通过相同的函数计算出相同的哈希地址,这种现象称为哈希冲突或哈希碰撞。
引起哈希冲突的一个原因可能是:哈希函数设计不够合理。
哈希函数设计原则:
定义域包括需要存储的全部关键码。
计算出的地址能均匀分布在整个空间中。
哈希函数应该比较简单。
常见哈希函数:
直接定址法
除留余数法
折叠法
随机数法
数学分析法
解决哈希冲突的常见两种方法是:闭散列和开散列。
闭散列:也叫开放定址法,当发生哈希冲突的时候,如果哈希表未装满,说明哈希表中还有空位置,那么可以把元素存放到表中的“下一个”空位中去。
常见的闭散列寻找空位的方法:线性探测,二次探测。
线性探测的处理为:从发生冲突的位置开始,依次继续向后探测,直到找到空位置为止。
采用闭散列处理哈希冲突时,不能随便删除哈希表中已经有的元素,如果直接删除,会影响其他元素的搜索,因为可能有多个元素是根据同一个哈希地址存放的,但是向后探测,删除前面的元素会导致后面的元素查找出错。
采用线性探测,实现起来非常简单,但是可能所有冲突连在一起,容易产生数据“堆积”。
那么如何缓解堆积呢?有人提出了一个叫负载因子的东西!
α = 填入表中的元素个数/散列表长度
α就是负载因子,我们可以根据负载因子的值控制哈希表,比如扩容。
开散列:开散列又叫链地址法。
开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一个子集,每个子集称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希中。