哈希表
1.定义
哈希表(Hash table),又称散列表,是一种高效的数据结构,它通过哈希函数将键(key)映射到值(value),实现快速的元素查询、添加和删除操作
看完它的定义后,第一个想到的是python的字典,学完后,确实是python的字典🤣
2哈希表常见操作
初始化、查询操作、添加键值对和删除键值对
/* 初始化哈希表 */
unordered_map<int, string> map;
/* 添加操作 */
// 在哈希表中添加键值对 (key, value)
map[12836] = "hello";
map[15937] = "my";
map[16750] = "cute";
map[13276] = "well";
map[10583] = "duck";
/* 查询操作 */
// 向哈希表中输入键 key ,得到值 value
string name = map[15937];
cout<<name<<endl;
/* 删除操作 */
// 在哈希表中删除键值对 (key, value)
map.erase(10583);
遍历哈希表
/* 遍历哈希表 */
// 遍历键值对 key->value
for (auto kv: map) {
cout << kv.first << " -> " << kv.second << endl;
}
// 使用迭代器遍历 key->value
for (auto iter = map.begin(); iter != map.end(); iter++) {
cout << iter->first << "->" << iter->second << endl;
}
3.哈希冲突与扩容
多个输入对应相同输出 多个输入对应相同输出哈希表的输入空间远远大于其输出空间,因此可能出现输入的东西不同却得到相同的输出,即“多个输入对应相同输出”,这种情况就称作哈希冲突。
解决策略
通过扩容哈希表来减少哈希冲突。
负载因子
负载因子(load factor)是哈希表的一个重要概念,其定义为哈希表的元素数量除以桶数量,用于衡量哈希冲突的严重程度,也常作为哈希表扩容的触发条件负载因子(load factor)是哈希表的一个重要概念,其定义为哈希表的元素数量除以桶数量,用于衡量哈希冲突的严重程度,也常作为哈希表扩容的触发条件
改良方式
哈希表的结构改良方法主要包括“链式地址”和“开放寻址”
链式地址:将每个桶的单个元素变成链表的形式,这样就可以填充更多的键值对,从而减轻了哈希冲突
链式地址的缺点:占用空间增大,查询效率降低
开放寻址(open addressing):不引入额外的数据结构,而是通过“多次探测”来处理哈希冲突,探测方式主要包括线性探测、平方探测和多次哈希等。
1.线性探测:
线性探测采用固定步长的线性搜索来进行探测,其操作方法与普通哈希表有所不同。
插入元素:通过哈希函数计算桶索引,若发现桶内已有元素,则从冲突位置向后线性遍历(步长通常为 1 ),直至找到空桶,将元素插入其中。
查找元素:若发现哈希冲突,则使用相同步长向后进行线性遍历,直到找到对应元素,返回 value 即可;如果遇到空桶,说明目标元素不在哈希表中,返回 None 。
注意点:1线性探测容易出现聚焦现象。2我们不能在开放寻址哈希表中直接删除元素
对于注意点的应对措施:采用“懒删除机制”利用一个变量TOMBSTONE来标记这个桶,来表示这个桶此时的状态为空,但是与None不同之处在于,遇到None会直接返回,而遇到TOMBSTONE会继续遍历。但是用懒删除又会出现新的问题:懒删除可能会加速哈希表的性能退化
2.平方探测:
平方探测与线性探测类似,都是开放寻址的常见策略之一。当发生冲突时,平方探测不是简单地跳过一个固定的步数,而是跳过“探测次数的平方”的步数,即1,4,9... 步。
优势:1.平方探测通过跳过探测次数平方的距离,试图缓解线性探测的聚集效应
2.平方探测会跳过更大的距离来寻找空位置,有助于数据分布得更加均匀。
注意点:1.仍然存在聚集现象,即某些位置比其他位置更容易被占用。
2.由于平方的增长,平方探测可能不会探测整个哈希表,这意味着即使哈希表中有空桶,平方探测也可能无法访问到它。
3.多次哈希
利用设定多个哈希函数,来查询元素
插入元素:若f1出现冲突,再尝试使用f2,以此类推,直到找到空位后插入元素
查找元素:在相同的哈希函数顺序下进行查找,直到找到目标元素时返回;若遇到空位或已尝试所有哈希函数,说明哈希表中不存在该元素,则返回 None
4.哈希算法
前面的链式寻址和线性探测都只能保证发生哈希冲突时候,哈希表的正常使用,无法减少哈希冲突的发生。
如果哈希冲突过于频繁,哈希表的性能则会急剧劣化
哈希算法的目标:1.均匀分布 2.确定性 3.效率高
哈希算法在其他领域的应用:1密码存储 2.数据完整性检查
对于密码学而言,为了防止使用逆向工程推出原始密码,哈希算法需要更高的安全特性:
1.单向性:无法通过哈希值反推出关于输入数据的任何信息。
2.抗碰撞性:应当极难找到两个不同的输入,使得它们的哈希值相同。
3.雪崩效应:输入的微小变化应当导致输出的显著且不可预测的变化。
注意点:“均匀分布”与“抗碰撞性”是两个独立的概念,满足均匀分布不一定满足抗碰撞性
(我对密码学倒是有兴趣,不过暂时没时间,希望未来可以学习)
哈希算法的设计
1.加法哈希:对输入的每个字符的 ASCII 码进行相加,将得到的总和作为哈希值。
2.乘法哈希:利用乘法的不相关性,每轮乘以一个常数,将各个字符的 ASCII 码累积到哈希值中。
3.异或哈希:将输入数据的每个元素通过异或操作累积到一个哈希值中。
4.旋转哈希:将每个字符的 ASCII 码累积到一个哈希值中,每次累积之前都会对哈希值进行旋转操作。(抽象)
注意点:使用大质数作为模数,可以最大化地保证哈希值的均匀分布
5总结
直接看原文好了,总结的挺到位的了
https://www.hello-algo.com/chapter_hashing/summary/