【数据结构】 哈希表 目录引言一、哈希表的基础概念1. 哈希映射2. 哈希函数设计直接定址法除留余数法数字分析法平方取中法二、哈希冲突及其解决方案1. 闭散列开放定址法线性探测二次探测伪删除与载荷因子2. 开散列链地址法 / 哈希桶结构优势扩容机制三、实战算法与海量数据处理中的应用1. 频次统计与查重2. 海量数据切割Hash Partition总结引言C98 提供的关联式容器std::map与std::set底层均采用红黑树实现保证元素有序的同时查找、插入、删除操作的平均时间复杂度为O ( log ⁡ N ) O(\log N)O(logN)。随着数据规模不断膨胀即使O ( log ⁡ N ) O(\log N)O(logN)也会因为树深度增加而带来不可忽视的比较次数。理想中的查找是不经过任何比较直接由关键码Key映射到存储位置。C11 中引入的unordered_map、unordered_set等无序关联容器正是基于这一思想通过哈希表将平均时间复杂度降至O ( 1 ) O(1)O(1)。一、哈希表的基础概念1. 哈希映射哈希表的核心在于哈希函数f ff它接受任意类型的关键码K KK输出一个非负整数f ( K ) f(K)f(K)该整数即为元素在底层数组中的下标。理想情况下每个关键码都对应唯一的下标插入与查找只需一次计算即可定位。例如假设一个哈希表底层数组长度为 10定义哈希函数f ( x ) x f(x) x \ % \ 10f(x)x则关键码 15 映射到下标 5关键码 23 映射到下标 3。查找时直接计算下标无需遍历比较。2. 哈希函数设计哈希函数的好坏直接影响哈希表的性能。设计的三个基本原则定义域必须覆盖所有可能的关键码。计算结果在值域中分布尽量均匀避免聚集。计算过程简单避免成为性能瓶颈。直接定址法取关键码的某个线性函数值为下标如H a s h ( K e y ) A × K e y B Hash(Key) A \times Key BHash(Key)A×KeyB。适用场景关键码集合连续且范围较小。例如用学生学号连续整数作为键存储学生信息可直接用学号作为数组下标。缺点若关键码分布稀疏会浪费大量数组空间。例如关键码只有 1 和 10000直接定址需要数组长度 10001中间位置全部闲置。除留余数法这是最常用的哈希函数公式为H a s h ( K e y ) K e y Hash(Key) Key \ % \ pHash(Key)Key。p 的选择通常取一个不大于哈希表长度m mm的质数且尽量远离 2 的幂次方。因为如果p pp是偶数奇偶性相同的 key 会集中映射若p pp接近2 n 2^n2n则哈希值只与 key 的低n nn位有关高位信息被丢弃分布性差。C STL 中unordered_map的默认桶数便是一组经过精心挑选的质数序列如 53、97、193、389、769……当负载因子超过阈值时会自动扩容到下一个更大的质数桶数。示例哈希表长度m 10 m 10m10取p 7 p 7p7质数且小于 10。Key 15 →15 15 \ % \ 7 115Key 22 →22 22 \ % \ 7 122// 冲突数字分析法设关键字是r进制数其各位上的数码共r种出现的频率可能不同某些数位上数码分布较为均匀各种数码出现的机会接近均等而另一些数位上分布不均仅有少数几种数码频繁出现。此时应选取那些数码分布较为均匀的数位以其组合构成散列地址。该方法适用于已知且固定的关键字集合若关键字集合发生变化则需重新构造新的散列函数。平方取中法顾名思义该方法取关键字平方值的中间几位作为散列地址。具体取多少位根据散列表大小和关键字范围确定。由于平方运算与关键字的每位都有关系因此使得散列地址的分布较为均匀。该方法适用于关键字的各位取值分布不均或关键字本身位数较少的情形。二、哈希冲突及其解决方案不同的关键码通过同一个哈希函数计算出相同地址的现象称为哈希冲突。冲突不可避免必须通过机制解决。解决方案分为两大类闭散列开放定址法和开散列链地址法。1. 闭散列开放定址法当发生冲突时若哈希表尚未填满则按某种探测序列在表中寻找下一个空闲位置存放元素。闭散列中的“闭”是指所有元素都存储在哈希表数组内部不借助外部结构。线性探测从冲突位置开始依次向后检查紧邻的下一个位置直到找到空位H i ( H 0 i ) H_i (H_0 i) % m,\quad i 0, 1, 2, \dotsHi​(H0​i)缺点容易产生聚集Primary Clustering。一旦某个区域出现连续被占用的位置后续插入的元素不论其初始哈希值落在何处只要探测到该区域前部就会被迫沿着聚集区向后延伸使聚集区进一步增长最终导致平均探测长度急剧增加。二次探测为缓解线性探测的聚集采用二次探测平方探测H i ( H 0 i 2 ) H_i (H_0 i^2) % m \quadHi​(H0​i2)或H i ( H 0 − i 2 ) \quad H_i (H_0 - i^2) % mHi​(H0​−i2)探测步长随冲突次数增加而平方增长有效避免相邻位置的连续堆积。但要求表长m mm必须是质数否则某些位置可能永不被探测到。(此处插入图片线性探测与二次探测对比图示)伪删除与载荷因子闭散列中不能直接物理删除元素否则会切断冲突元素的探测路径例如若直接清空某位置后续元素按照探测序列查找时会因遇到空位而提前终止导致误判“不存在”。通用做法是采用标记删除为每个位置设置三种状态EMPTY、EXIST、DELETE。查找时遇到DELETE继续向后探测插入时可以覆盖第一个遇到的状态为EMPTY或DELETE的位置。载荷因子α \alphaα 表中元素个数 / 表长。当α \alphaα超过 0.7~0.8 时冲突概率和探测长度会呈指数级上升必须进行扩容并进行重新散列将旧表所有元素重新计算哈希值插入新表。闭散列必须严格控制载荷因子。例题将关键字序列(7,8,301118,9,14)散列存储到散列表中。散列表的存储空间是一个下标从 0 开始的一维数组散列函数为 H(key)(keyx3)mod 7处理冲突采用线性探测再散列法要求装填载因子为0.7。请画出所构造的散列表。2. 开散列链地址法 / 哈希桶开散列是 C STL 中unordered_map采用的方案。哈希表本身是一个指针数组每个数组元素是一个“桶”Bucket的头指针哈希值相同的元素被链接到同一个桶下的单链表中。例如关键字序列 19, 14, 23, 01, 68, 20, 84, 27, 55, 11, 10, 79 ,散列函数 H(key)key%13结构优势闭散列必须预留大量空位以保证低探测长度空间利用率低开散列允许载荷因子大于 1桶链表增长只会线性影响该桶查找不会干扰其他桶。冲突被限制在桶内不会引发全局性聚集。虽然链表指针会带来额外内存开销但整体空间效率优于闭散列。扩容机制随着元素增加单个桶链表可能变长查找效率退化向链表O ( K ) O(K)O(K)。现代实现通常在载荷因子等于 1 元素总数等于桶数时触发扩容创建一个更大的桶数组通常大小翻倍并取下一个更大的质数。遍历原表所有桶的链表对每个节点重新计算哈希值h h a s h ( k e y ) h hash(key) % new_bucket_counthhash(key)将其转移到新表对应桶的头部头插法O ( 1 ) O(1)O(1)不需要重新申请节点内存。交换新表与原表旧表析构。因为只是指针移动避免了大规模对象拷贝扩容效率较高。三、实战算法与海量数据处理中的应用1. 频次统计与查重利用unordered_map和unordered_set可在O ( N ) O(N)O(N)时间内解决经典问题。统计重复元素找出数组中出现次数超过⌊ N / 2 ⌋ \lfloor N/2 \rfloor⌊N/2⌋的多数元素。intmajorityElement(vectorintnums){unordered_mapint,intcnt;for(intv:nums){if(cnt[v]nums.size()/2)returnv;}return-1;}求两个数组的交集vectorintintersect(vectorintnums1,vectorintnums2){unordered_setintset(nums1.begin(),nums1.end());vectorintres;for(intv:nums2){if(set.erase(v)){// 查找并删除避免重复res.push_back(v);}}returnres;}2. 海量数据切割Hash Partition当数据文件远超内存时利用哈希切割实现“分而治之”。场景100GB 日志文件每条记录含 IP 地址统计出现次数最多的 IP。步骤设定小文件数量N NN如 1000。逐行读取日志提取 IP计算H a s h ( I P ) Hash(IP)\ %\ NHash(IP)将该行追加到对应编号的小文件中。切割完成后相同的 IP 必然位于同一个小文件中。对每个小文件加载到内存使用unordered_map统计 IP 频次得到该文件的 Top1 IP。汇总各文件的 Top1 IP找出全局最频繁的 IP。哈希切割保证了相同记录聚合在一起使得内存中完成精确统计成为可能是解决大数据经典面试题的基石。总结哈希表以空间换时间通过精心设计的哈希函数和冲突解决机制达到平均O ( 1 ) O(1)O(1)的查找性能。除留余数法 链地址法是工业级实现如 C STL的主流组合。闭散列需控制载荷因子并使用伪删除开散列允许负载因子大于 1扩容时通过移动指针高效转移。位图和布隆过滤器是在内存受限场景下的强大概率型工具适用于去重、存在性检查及缓存穿透防御。处理海量数据时哈希切割提供了一种可并行的分治策略将大问题转化为小规模精确统计。

本月热点