1. 链地址法(Separate Chaining,又称拉链法)
-
原理:
每个哈希桶(数组中的元素)维护一个链表(或红黑树),所有哈希到同一位置的键值对以链表形式存储。 -
实现方式:
-
链表:冲突的元素直接追加到链表中(如 Java 8 之前的
HashMap
)。 -
红黑树:当链表长度超过阈值时,转为红黑树以提高查询效率(如 Java 8 后的
HashMap
)。
-
-
优点:
-
实现简单,冲突处理直观。
-
适合高负载因子(元素多,空间少)的场景。
-
-
缺点:
-
链表过长时查询效率下降(时间复杂度退化为 O(n))。
-
需要额外存储指针,空间开销略大。
-
-
应用场景:
-
Java
HashMap
、Pythondict
(内部优化版本)、Go 语言哈希表。
-
2. 开放寻址法(Open Addressing)
-
原理:
所有元素直接存储在哈希表的数组中,当发生冲突时,按照某种探测策略(如线性探测、二次探测)寻找下一个空闲位置。 -
探测策略:
-
线性探测(Linear Probing):
依次检查下一个位置(hash(key) + i) % capacity
,直到找到空槽。-
缺点:容易导致“聚集”(Clustering),即连续被占用的位置形成长块,降低插入和查询效率。
-
-
二次探测(Quadratic Probing):
探测步长为二次函数,如(hash(key) + i²) % capacity
,减少聚集问题。 -
双重哈希(Double Hashing):
使用第二个哈希函数计算步长,如(hash1(key) + i * hash2(key)) % capacity
。
-
-
优点:
-
无需额外数据结构,内存利用率高。
-
适合数据量较小或内存敏感的场景。
-
-
缺点:
-
负载因子较高时(接近 1),性能急剧下降。
-
删除操作复杂(需标记为“已删除”而非直接清空)。
-
-
应用场景:
-
Redis 哈希表(结合渐进式 rehash)、C++
std::unordered_map
(某些实现)。
-
3. 再哈希(Rehashing)
-
原理:
当发生冲突时,使用第二个哈希函数重新计算位置,直到找到空闲槽。 -
特点:
-
需要预定义一组哈希函数(如双重哈希)。
-
可以有效减少聚集问题,但计算成本较高。
-
-
应用场景:
-
对哈希质量要求较高的场景,如分布式系统中的一致性哈希。
-
4. 公共溢出区(Overflow Area)
-
原理:
将哈希表分为两部分:-
主表:正常存储无冲突的元素。
-
溢出区:所有冲突的元素统一存储在溢出区(通常是一个链表或另一个数组)。
-
-
优点:
-
主表结构简单,查询速度快。
-
-
缺点:
-
溢出区可能成为性能瓶颈(尤其是冲突较多时)。
-
-
应用场景:
-
早期数据库索引、文件系统目录管理。
-