深入哈希表(一)--哈希表的简介(Hash_Table)

一、理解Hash_Table

哈希表是从集合A到集合B的映射,可被视为一种字典结构,这种结构用意在于提供常数时间的基本操作。

理想情况下,一次就能直接从哈希表中搜索到需要的元素,因为我们在元素的存储位置和它的关键码之间建立了一个对应的函数关系。

我们可以通过这个函数关系,在插入的时候按计算出来的位置存放,在读取元素的时候通过这个函数进行计算位置,在对找到的位置进行读取即可。

哈希并不等于哈希表,哈希是算法,字符串哈希算法、一致性哈希也是哈希。

我们对key的要求是能转成整形,因为我们需要求余。

Hash_Table也有缺陷,缺陷就是增容的时候很麻烦。

二、哈希冲突

由于不同的key值经过哈希函数处理后,可能拥有相同的哈希地址,这种情况我们称之为哈希冲突,任何的散列函数都不能避免哈希冲突的产生。

三、负载因子

深入哈希表(一)--哈希表的简介(Hash_Table)_第1张图片

四、质数表

使用质数作为除数可以减少哈希冲突,hash表最好使用质数来设计表格大小,先将28个质数计算好(近似二倍的关系),放在一个地方备用给访问,并且提供一个函数来查询这28个质数,找到最接近某数并且大于某数的质数。

在STL中给出的28个质数:

深入哈希表(一)--哈希表的简介(Hash_Table)_第2张图片

注:

ul是无符号的long。如果存入42亿的值还需要扩容,一般是用不到的,因为没有那么大的内存可以存储这么多的东西,光哈希表就占了几十个G内存。给成静态的是因为只创建一份质数表。

获取下一个质数的方法:

  1. 定义一个变量为28,防止扩容。
  2. 把质数表放入。
  3. 传入一个value,循环遍历素数表,如果大于当前的值,返回素数表中的值,如果没找到直接返回最后一个值

五、常见散列函数的选取方法

(1)直接定地法

直接定址法的意思就是我们通过一个线性的函数作为散列函数。
例如:Hash(key)=A*key+2B。

(2)除留余数法

设散列表中允许的地址数为m,取一个不大于m,但接近或者等于m的质数p作为除 数,按照哈希函数:Hash(key) = key % p p<=m,将关键码转换成哈希地址。

(3)随机数法

选取一个随即函数,取关键字的随机函数值作为它的哈希地址。

(4)折叠法

折叠法是将关键字从左到右分割成位数相等的几部分(注意:后一部分位数不够时可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。

(5)数学分析法

(6)平方取中法

六、散列冲突处理方法

开放定址法–闭散列方法

这几个方法写在一起可以用命名空间进行区分。

1、线性探测

设给出一组元素,它们的关键码为:37,25,14,36,49,68,57,11,散列表为HT[12],表的大小m = 12,假设采用Hash(x) = x % p; // (p = 11) 11是接近m的质数,就有:

Hash(37) = 4、 Hash(25) = 3、 Hash(14) = 3、 Hash(36) = 3、Hash(49) = 5、Hash(68) = 2、Hash(57) = 2、Hash(11) = 0

采用线性探查法处理冲突

这里写图片描述

缺点:会导致一片位置冲突,在一片范围内冲突越来越多,从而导致堆积问题。

2、二次探测

所以为了缓解冲突,采用了二次探测,二次探测是加i的i平方,又有人提出了新的探测方法,双散列方法,具体是左右摇摆走。
线性探测的实现:
基本实现与线性探测一样,不同的是插入index加i的平方,保存first,(用first+i的平方)%size,最后再加++i,既是二次探测。

开散列方法

3、开链法(哈希桶)

做法是在每一个表格元素中维护一个list,然后我们在list身上进行元素的插入和搜索删除等操作。虽然对list进行搜索是一种线性操作,但是只要list足够短,速度还是很快。

总结:

  • 开散列方法优于闭散列的方法。
  • 散列函数中除留余数法优于其他类型的算法

你可能感兴趣的:(数据结构)