【哈希结构、哈希应用、unorder系列关联式容器】

一、以哈希为底层结构的unordered系列关联式容器

因为以红黑树为底层结构的,在树中结点非常多的情况下,其查询效率也不是非常的理想,所以c++11中又提供了底层结构为哈希结构的查询效率较高的容器
1、unordered_map
(1)特点:

  • 存储键值对
  • 键和值的映射类型可能不同
  • 通过key访问value的速度比map要快,但是迭代效率比较低
  • 实现了中括号[]的重载
  • 至少有向前的迭代器
    (2)unordermap和map的对比:
  • map:
    优点:
    有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作;
    内部实现一个红黑树使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高;
    缺点:
    空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间;
    适用处:
    对于那些有顺序要求的问题,用map会更高效一些;

unordered_map:
优点:
因为内部实现了哈希表,因此其查找速度非常的快 缺点: 哈希表的建立比较耗费时间;
适用处:
对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map;
2、unordered_set
优缺点同上;

二、底层结构(哈希)
1、什么是哈希?

  • 为了可以跳脱出树在查找元素时必须经过多次关键码比较,改善树的搜索效率取决于搜索过程中元素的比较次数;

提出了一种理想化的搜索方式:

  • 不经过任何比较,依次直接从表中得到要搜索的元素; 构造这种结构,使得元素的存储位置与它的关键码之间能够建立一一的映射关系,将这种方式成为哈希(散列)方法;
    其中哈希(散列)方法中使用的转换函数称为哈希(散列)函数,构造出来的结构叫做哈希表(散列表);

2、哈希函数的设计原则:

(1)哈希函数的定义域必须包括要存储的全部关键码,若表允许有m个地址时,其值域必须在0~m-1之间;
(2)哈希函数计算出来的地址可以均匀的分布到整个空间里;
(3)哈希函数的设计应该要比较简单;

  • 常见哈希函数

1)直接定制法
关键字的某个线性函数为散列地址:Hash(key)=A*key+b;
优:简单、均匀
缺:事先知道关键字的分布情况
适合:查找比较小且连续的情况
2)除留余数法
若散列表的允许地址数为m,取不大于m的质数p作为除数:Hash(key)=key%p(p<=m),将关键码转换成哈希地址;
3)平方取中法
即对关键字进行平方处理,取中间的若干位作为哈希地址;
适合:不知道关键字的分布,而位数又不是很大的情况
4)折叠法
将关键字从左到右分割成位数相等的几个部分,将这几个部分进行叠加求和,并按照散列表的表长,取最后几位作为散列地址;
适合:事先不知道关键字的分布,适合关键字位数比较多的情况

3、哈希冲突

两个数据元素的关键字不相同,但是经过哈希函数转换后计算出的哈希地址是相同的,称这种现象为** 哈希冲突或者哈希碰撞**;

4、如何处理哈希冲突?

1、指定合适的哈希函数
(因为引起哈希冲突的一个可能原因就是:哈希函数的设计不够合理
2、闭散列
(1)什么是闭散列?
闭散列也叫开放地址法,当发生哈希冲突的时候,如果哈希表未被装填满,则将key存放在冲突位置的下一个空位置中去
(2)闭散列怎么寻找发生冲突的下一个空闲位置呢?
a)线性探测
从发生冲突的位置开始,依次向后探测,直到找到下一个空闲位置为止;
注:闭散列处理哈希冲突,不能随便物理删除哈希表中已有的元素,因为会影响其他元素的搜索;

  • 什么时机增容?如何增容?
    装填因子应该控制在0.7-0.8以下,超过0.8则进行增容;
    优点:实现非常简单
    缺点:一旦发生哈希冲突,所有冲突连在一起,容易造成数据“堆积”,导致搜索效率降低;

(实现代码见文尾链接)
b)二次探测
(线性探测的缺陷是产生冲突的数据堆积在一块)
而二次探测的方式就避免了这个缺陷,其查找下一个空位置的方法为:Hi=(H0 +/- i^2)%m
在这里插入图片描述

注:当表的长度为质数且表的装载因子(a=填入表中的元素个数/哈希表的长度)不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探测两次。因此只要表中有一半的空位置,就不会存在装满的问题;故在插入时必须要确保装填因子a不超过0.5,超出则要增容

  • 闭散列缺点:空间利用率比较低,同样也是哈希的缺陷;

3、开散列

(1)什么是开散列?
开散列又叫链地址法,先对关键码集合利用哈希函数计算地址,将具有相同地址的关键码归为同一个子集合,每一个子集和称为一个桶,各个桶中的元素通过一个单链表链接起来,各个链表的头结点存储在哈希表中
在这里插入图片描述
(代码实现见文尾链接)

  • 开散列(链地址法)增容

1)为什么要增容?
因为桶的个数是一定的,随着元素的不断插入,桶中的元素个数会不断增多,极端情况下,可能会导致桶中链表节点非常多,影响哈希表性能;
2)什么时候进行增容?
每个哈希桶中刚好挂一个结点,再继续插入元素的时候,每一次都会发生哈希冲突,故在元素个数刚好等于桶的个数的时候,可以给哈希表进行增容;

4、开散列与闭散列比较结果

链地址法处理溢出,需要增加链接指针,但是相比于开放地址法,必须保持足够大的空间来减少冲突,提高搜索效率,反而链地址法更加节省存储空间;

代码https://github.com/Wilingpz/sunny/tree/master/Hash

三、哈希的应用
1、位图

(1)什么是位图?
位图是每一位用来存放某种状态,适用于海量数据,数据无重复的场景,来判断某个数据是否存在于次海量数据中
(2)位图的应用
a)快速查找某个数据是否在一个集合中
b)排序

关于排序,已经有多种排序方法了:插入排序,归并排序,快速排序,希尔排序等。每种排序都有不同的用武之地。为什么需要位图排序呢

所有的内部排序(上述所提及)都必须一次性将所有排序元素载入内存。假如有1000,000个整数,每个整数4字节,则意味着,至少需要4000,000B约为 4MB 的内存空间, 如果仅仅只有 1MB 的内存空间可用,那么,应该怎么办呢?

答:位图技术,就是将问题映射到位串上,对位串进行处理后,再将位串逆射到问题空间上。具体而言, 假设要对数组 不大于 20 的元素数组 [5, 2, 12, 18, 7, 9, 13, 19, 16, 4, 6] 进行排序, 则可以将其映射到位串 11010011001001110100 ,其中, 1 表示数组元素出现的位置(最高位在后面,最低位在左边,以下标0起头),然后,从低位往高位扫描, 即可得到 { 2, 4, 5, 6, 9, 12,13,16,18,19} 这样就排序好了。根据位图技术, 1000,000 个互不重复的整数数组的排序, 只需要大约 1000,000 b = 0.125MB 内存空间。

c)求两个集合的交集、并集
d)操作系统中的磁盘标记

2、布隆过滤器

(1)什么是布隆过滤器?
由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
在这里插入图片描述(2)布隆过滤器的查找
因为一个元素会经过多个哈希函数映射到一个位图上;
所以布隆过滤器在查找的时候:分别计算每个哈希值对应的比特位置存储的是否为0,只要有一个为0,则代表该元素一定不在哈希表中,否则就有可能在哈希表中;(存在一定程度的误判)
(3)布隆过滤器的删除
不支持直接的删除工作,因为可能会影响到其他元素;
也可以将布隆过滤器中的每个比特位扩展成一个小的计数器,插入一个元素给该位进行+1操作,删除一个元素对其进行-1操作;(但是会增加几倍的存储空间,也无法确认元素是否真的存在于布隆过滤器中,存在计数回绕)
(4)布隆过滤器的优缺点
优点:

  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关;
  2. 哈希函数相互之间没有关系,方便硬件并行运算;
  3. 布隆过滤器不需要存储元素本身,对内容具有保密性;
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能;
  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算;
    缺点:
  7. 有误判率,不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据)
  8. 不能获取元素本身
  9. 一般情况下不能从布隆过滤器中删除元素
  10. 如果采用计数方式删除,可能会存在计数回绕问题

hash增删改查的时间复杂度都是都是o(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值