Hash——哈希

本文介绍了哈希的基本概念,哈希冲突及其解决方法。哈希函数的设计原则包括值域限制、均匀分布和简单性。直接定址法和除留余数法是常见的哈希函数类型,但都可能存在冲突。闭散列,如线性探测和二次探测,用于解决冲突,但可能导致空间利用率低。开散列,即链地址法,通过链表解决冲突,允许表的增容以保持性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

哈希概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O( ),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

哈希冲突

不同关键字通过相同哈希函数计算出相同的哈希地址,该现象称为哈希冲突或者哈希碰撞。

哈希函数

哈希函数设计原则:

  1. 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间。
  2. 哈希函数计算出来的地址能均匀在整个空间中
  3. 哈希函数应该比较简单。
1.直接定址法

适合查找比较小且连续的情况。
优点:速度快,且节省空间
缺点:

  1. 如果数据范围很大,直接定址法会浪费很多空间。
  2. 不能处理浮点数,字符串等场景。
2.除留余数法

主要解决数据范围很大的问题。但是有可能出现哈希冲突。
优点:将数据映射到优先的空间里。
缺点:有可能将不同的数映射到相同的位置。

3.其他哈希函数
  1. 平方取中:假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址;平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
  2. 折叠法:折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
  3. 随机数法:选择一个随机函数,取关键字的随机函数值为它的哈希地址。通常应用于关键字长度不等时采用此法
  4. 数学分析法

哈希冲突解决

解决哈希冲突最常见的两种方法是闭散列(开放定址法)和开散列

闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放在冲突位置的下一个空位置中。

线性探测

从发生冲突的位置开始,依次向后探测,知道寻找到下一个空位置为止。
缺点:可能在连续位置出现冲突,造成踩踏现象(洪水)
插入

  1. 通过哈希函数获取待插入元素在哈希表中的位置。
  2. 如果该位置中没有元素,那么就直接插入元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素。
    在这里插入图片描述
    删除
    **采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。**比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。
// 哈希表每个空间给个标记 
// EMPTY此位置空, EXIST此位置已经有元素, DELETE元素已经删除
enum State{
   EMPTY, EXIST, DELETE};
二次探测

在这里插入图片描述
在这里插入图片描述
研究表明:当表的长度为质数且表因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。
因此:闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

// 注意:假如实现的哈希表中元素唯一,即key相同的元素不再进行插入
// 为了实现简单,此哈希表中我们将比较直接与元素绑定在一起
template<class K, class V>
class HashTable
{
   
	 struct Elem
	 {
    
	 pair<K, V> _val;
	 State _state;
	 };
 
	public:
		 HashTable(size_t capacity = 3)
		 : _ht(capacity), _size(0)
		 {
   
		 for(size_t i = 0; i < capacity; ++i)
		 _ht[i]._state = EMPTY;
		 }
 
	 bool Insert(const pair<K, V>& val)
	 {
   
		 // 检测哈希表底层空间是否充足
		 // _CheckCapacity();
		 size_t hashAddr = HashFunc(key);
		 // size_t startAddr = hashAddr;
		 while(_ht[has
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值