【C++进阶】哈希 + unordered系列容器

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


一、哈希思想

哈希是一种 映射 思想。它是将存储的值跟存储的位置建立映射关系,由这种思想而构成的数据结构称为 哈希表(散列表)

哈希表中插入数据和查找数据 的步骤如下:

  • 插入数据:根据当前待插入的元素的键值,通过哈希函数计算出哈希值,并存入相应的位置中

  • 查找数据:根据待查找元素的键值,计算出哈希值,判断对应的位置中存储的值是否与键值相等

例如:数据集合{1,7,6,4,5,9},哈希函数设置为:hash(key) = key % capacitycapacity为存储元素底层空间总的大小)

在这里插入图片描述

显然,这个哈希表并没有把所有位置都填满,数据分布无序且分散

因此,哈希表又称为散列表

那么如何建立映射关系呢?这就要涉及到哈希函数了。

二、常见的哈希函数

2.1 直接定址法

函数原型:Hash(key) = A * key + BA, B为常数)

  • 适用场景:值的分部范围比较集中。例如:统计字符字符出现的次数。
  • 缺点:需要提前知道键值的分布情况

2.2 除留余数法

函数原型:Hash(key) = key % m m为哈希表的大小)

  • 适用场景:范围不集中,分布分散的数据
  • 缺点:容易出现哈希冲突,需要借助特定方法解决

三、哈希冲突

3.1 哈希冲突的原因

哈希冲突:又称哈希碰撞,不同的值可能会映射到同一个位置。

在这里插入图片描述

如果继续插入元素 44哈希值 hash(44) = 44 % 10 = 4,此时哈希值为 4 的位置处已经有元素了,无法继续存入,此时就发生了 哈希冲突。

在这里插入图片描述

3.2 如何解决哈希冲突

闭散列(开放定址法)

当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的下一个空位置中去。

那么如何寻找下一个空位置呢?

  • 线性探测法:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止

例如,我们用除留余数法(hash(key)=key%10)将序列{1, 6, 10, 1000, 101, 18, 7, 40}插入到表长为10的哈希表中,插入过程如下:

在这里插入图片描述

通过上图可以看到,随着哈希表中数据的增多,产生哈希冲突的可能性也随着增加,最后在40进行插入的时候更是连续出现了四次哈希冲突。于是如果哈希表接近满的话,插入、查找、删除的效率都会越来越低。

  • 优化方案:二次探测,每次向后探测 i ^ 2。尽管如此,闭散列的效果还是不尽人意,实际中还是 开散列 用的更多一些
开散列(链地址法、开链法、哈希桶)

所谓 开散列 就在原 存储位置 处带上一个 单链表,如果发生 哈希冲突,就将 冲突的值依次挂载即可。因此也叫做 链地址法、开链法、哈希桶。

例如,我们用除留余数法将序列{1, 6, 15, 60, 88, 7, 40, 5, 10}插入到表长为10的哈希表中,当发生哈希冲突时我们采用开散列的形式,将哈希地址相同的元素都链接到同一个哈希桶下,插入过程如下:

在这里插入图片描述

  • 开散列 中进行插入时,如果对应位置的哈希值被占了,那么就在对应位置开一块链表进行存储。
  • 开散列中进行查找时,需要先根据哈希值找到对应位置,并在单链表中进行遍历。

一般情况下,单链表的长度不会太长的,因为扩容后,整体长度会降低。如果单链表真的过长了,我们还可以将其转为红黑树,此时效率依旧非常高。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值