哈希表

哈希表

1.什么是哈希表:

是根据键(Key)而直接访问在内存存储位置的数据结构,也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。
它是一种数据结构:并且是在不用进行数据比较的情况下查找数据,时间复杂度为O(1),让数据与其存储的内存空间建立一一对应的关系,时间复杂度为O(1)。

2.具体内容
  • 哈希表: 可以在 O(1) 的时间复杂度内找到元素;

  • 哈希函数: Hash(key) = key % capacity;

  • 哈希冲突: 不同的关键字计算出相同的哈希地址;(这是哈希表无法解脱的桎梏,一旦使用哈希表,哈希冲突是无法避免的,只能通过巧妙的设计哈希函数,尽量减少哈希冲突,而无法避免)

假设存储下面这10个元素:
哈希表_第1张图片
通过哈希函数计算出存储地址,头插到链表中。相当于给每个桶中挂一个链表,为何要头插呢?提高效率,不用遍历链表。

那如果哈希函数是:Hash(key) = key % 10;并且存储的数的个位数相同,这样就形成了哈希冲突。

那么问题来了:什么是哈希冲突?
多个不同的元素,通过相同的哈希函数计算出相同的哈希地址。(也叫哈希碰撞。)

3.如何解决哈希冲突?

1、闭散列:

  • 1.哈希函数:如果哈希函数设置的不合理,冲突概率就会特别大。

      设计哈希函数准则:
     1. 哈希函数值域涵义:是表格的下标,哈希函数值域不能超过哈希表的范围;
     2. 产生的哈希地址应该尽可能均匀;
     3. 设置的函数尽可能简单。
    

    常见的哈希函数:

    1、直接定制法(常用):
    取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关键字的分布情况。 使用场景:适合查找比较小且连续的情况;
    2、除留余数法(常用):
    设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址。
    3、 平方取中法:
    假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
    4、 折叠法:
    折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。
    折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
    5、 随机数法:
    选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。通常应用于关键字长度不等时采用此法

  • 从哈希冲突的位置找“下一个”空位置。
    (1)找下一个空位置的方法:
    1、线性探测:逐个挨着往后依次查找;(冲突容易形成一片)
    2、二次探测:假设在第H(0)位置发生冲突,H(i) = H(0) + i^2;(i)表示第i次探测。
    优点:二次探测发生哈希冲突,在探测时每次计算的哈希地址比较离散(不会像线 性探测挨在一起), 因此可以解决线性探测中存在的数据堆积问题。
    缺点:如果哈希表中空余位置比较少,二次探测在找空位置期间可能需要找许多次,而线性探测在一圈之内肯定可以找到空位置,而对于闭散列,元素多到一定程度,哈希表发生冲突的概率会急剧上升。这时候,我们会考虑重新扩容—重新扩容—>注意:哈希表扩容时机–不会等到表格中元素存满的情况下扩容—>原因:如果表格中空位置比较少的情况下,发生哈希冲突的概率会急剧上升,影响哈希表的性能。

哈希的负载因子:存储元素 / 表格容量

线性探测:0.7左右
二次探测:0.6左右
闭散列缺陷:空间利用率比较低。

2、开散列(开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中)。
哈希表_第2张图片
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。

哈希的应用
  • 1、位图
    所谓位图,就是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。
    例:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。(我们就可以用位图解决)。
    数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。
  • 布隆过滤器:
    布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。

思想:将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。
但是:布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。

布隆过滤器优点:

  • . 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
  • 哈希函数相互之间没有关系,方便硬件并行运算;
  • 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势;
  • . 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势;
  • 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能;
  • 使用同一组散列函数的布隆过滤器可以进行交、并、差运算;

布隆过滤器缺陷:

  • 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白名单,存储可能会误判的数据);
  • .不能获取元素本身;
  • 一般情况下不能从布隆过滤器中删除元素;
  • 如果采用计数方式删除,可能会存在计数回绕问题;

你可能感兴趣的:(c++)