哈希表总结(一致性哈希)

哈希表是一种通过哈希函数将特定的键映射到特定值的一种数据结构,他维护着键和值之间一一对应关系。

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

装填因子(Loading Factor):设散列表空间大小为m,填入表中元素个数是n,则称α=n/m为散列表的填装因子。

解决冲突的办法:

(一)拉链法

  一个散列函数能够将键转化为数组索引。散列算法的第二步就是碰撞处理,也就是处理两个或多个键的散列值相同的情况。拉链法就是将大小为M 的数组中的每个元素指向一条链表,链表中的每个结点都存储了散列值为该元素的索引的键值对。这个方法的思想就是选择足够大的M,使得所有链表都尽可能短以保障高效的查找,否则容易退化成单链表的情况。如下图中M=5:


具体可参考:http://www.nowamagic.net/academy/detail/3008060

优点:

  • 拉链法处理冲突简单,且无堆积现象,即非同义词不会发生冲突(开放地址法会发生冲突),因此平均查找长度较短
  • 由于拉链法中各链表上的结点空间都是动态申请的,故它更适合于造表前无法确定表长的情况
  • 删除结点的操作易于实现
  • 开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α>=1,且结点较大时,拉链法中的指针域可以忽略不计,因此可以节省空间

缺点:

  • 在对链表进行存储空间分配的时候,会降低整个程序的运行效率
  • 指针需要额外的空间,当结点规模较小时,开放定址法较为节省空间

(二)开放定址法

这个方法的基本思想是:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。这个过程可用下式描述: 
H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1)) 
其中: H ( key ) 为关键字 key 的直接哈希地址, m 为哈希表的长度, di 为每次再探测时的地址增量。 
采用这种方法时,首先计算出元素的直接哈希地址 H ( key ) ,如果该存储单元已被其他元素占用,则继续查看地址为 H ( key ) + d 2 的存储单元,如此重复直至找到某个存储单元为空时,将关键字为 key 的数据元素存放到该单元。 
增量 d 可以有不同的取法,并根据其取法有不同的称呼: 
( 1 ) d i = 1 , 2 , 3 , …… 线性探测再散列; 
( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2…… 二次探测再散列; 

( 3 ) d i = 伪随机序列 伪随机再散列; 

需要注意的是:

  • 在线性探测过程中会产生数据聚集问题,当数据聚集越来越大时,数据经哈希化后就需要插在聚集的后端。这样会使得效率变得很低。二次探测是防止聚集产生的一种尝试,相隔比较远的单元进行探测,而不是线性一个个的探测。
  • 当哈希表中接近被填满时,向表中插入数据效率就会很低,当hash完全被填满时,在这之前应该对数组进行扩展,将表中数据转移(一般为了保证查找效率,散列表的使用率不会超过1/2)。由于数组具有固定大小,不能随意变化。常用的就是新建数组复制数据,一般可以选择将新建的数组扩展为原来的两倍,更好的选择是将数组长度设置为一个质数。计算新数组的容量是重新hash计算的一部分。java中vector类就是采用扩展数组的方式。
  • 再哈希法:再哈希是把关键字用不同的哈希函数再做一遍哈希化,用这个结果作为步长,对指定的关键字,探测的步长是不变的,可以说不同的关键字可以使用不同的步长,并且步长可以控制。步长是不能为零的,不然会形成死循环。可采用如下:stepSize=constant-(key%constant);
  • hash表的容量(即数组长度)通常设置成一个质数,假设hash表的长度不是一个质数而为15,那么对于那些映射在数组单元0的数字步长为5,就会探索在0→5→10→0→5→10→0一直循环下去,在这三个位置进入死循环,如果数组容量为11,这样就可以探测到所有的单元。

(三)建立公共溢出区

    这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表.(注意:在这个方法里面是把元素分开两个表来存储)    

 总结:   

    由于哈希表高效的特性,查找或者插入的情况在大多数情况下可以达到O(1),时间主要花在计算hash上,当然也有最坏的情况就是hash值全都映射到同一个地址上,这样哈希表就会退化成链表,查找的时间复杂度变成O(n),但是这种情况比较少,只要不要把hash计算的公式外漏出去并且有人故意攻击(用兴趣的人可以搜一下基于哈希冲突的拒绝服务攻击),一般也不会出现这种情况。

     相对于二叉查找树,散列表的优点在于代码更简单,且查找时间最优(常数级别,只要键的数据类型是标准的或者简单到我们可以为他写出满足均匀性假设的高效散列函数即可)二叉查找树相对于散列表的优点在于抽象结构更加简单(不需要设计散列函数),红黑树可以保证最坏的情况下的性能且它能够支持的操作更多(如排名,选择,排序和范围查找)。

    一般情况下我们会选择哈希表,其他因素更重要时我们才会选择红黑树。

参考:https://www.jianshu.com/p/dbe7a1ea5928

          https://blog.csdn.net/w_fenghui/article/details/2010387

          http://www.nowamagic.net/academy/detail/3008050(易于理解)

          https://blog.csdn.net/cai2016/article/details/52728761?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

          https://www.cnblogs.com/s-b-b/p/6208565.html

         https://blog.csdn.net/qq_21688757/article/details/53861896(对哈希表有简单的认识)

 补充:一致性哈希算法(用于解决服务器均衡问题);

一致性哈希算法是对 2^32 取模,我们把这个由2的32次方个点组成的圆环称为 hash环。

因为从图片的位置开始,沿顺时针方向遇到的第一个服务器 就是A服务器,所以,上图中的图片将会被缓存到服务器A上

 参考链接:https://blog.csdn.net/justloveyou_/article/details/78313649

猜你喜欢

转载自blog.csdn.net/u012864854/article/details/79769885