哈希函数

哈希函数(Hash function)又称散列算法、散列函数,是一种从任何一种数据中创建数字指纹的方法。

特征

一个好的哈希函数应该满足以下几种特性:

  • 快速:计算速度足够快
  • 确定性:对于相同的输入,总是产生相同的输出
  • 离散性:对于有规律的数据输入,应该输出无规律的结果
  • 不可逆:无法从哈希值逆向推算出原数据
  • 难以碰撞:难以找到两个不同的数据计算后输出结果相同(概率很小,小到几乎不可能)

哈希函数

哈希表

哈希表(Hash table)又称散列表,是根据键(Key)而直接访问在记忆体储存位置中值(Value)的数据结构。

创建一个数组,使用哈希函数对所有数据进行压缩生成与之对应的哈希值,再将生成的哈希值与数组长度取模确认数据保存的位置。

哈希值取模

哈希碰撞

由于将无限的数据映射到有限的集合中,难免出现多个数据的哈希值相同的情况,这种情况叫做哈希碰撞
不同的数据出现相同的哈希值,取模的结果也必定是相同的,经典哈希表使用链表将多个数据放在一起

哈希碰撞

表扩容

随着数据无限插入,链表的长度也会随之增长,链表越长查询效率就会越低。
通常会给哈希表设置一个阈值,当表中元素数量达到阈值时触发扩容机制,将表的大小扩容到原先的 $2$ 倍。

  • 创建一个新的数组,长度是原来的两倍
  • 便利原先哈希表中所有的元素,重新计算哈希值取模放入新的数组中
  • 使用新的数组替换原先的数组,完成哈希表的扩容

时间复杂度

假设阈值为每个链表长度不超过 $2$,输入 $N$ 个数据所需要的扩容次数为 $O(logN)$ 级别。
由于每次扩容都需要将表中所有的元素都遍历一遍,复杂度为 $O(N)$ 级别。
所以整个哈希表扩容的复杂度为 $O(NlogN)$ 级别。

假设链表的长度为 $k$,则理论上查询时的复杂度为 $O(k)$。
如果这个 $k$ 非常小,小到几乎可以忽略不记时,查询的复杂度就能近似的认为是 $O(1)$ 级别