1. Hash Table

​ 我们都知道,二叉树、红黑树等数据结构,它们的查找都是先从根节点开始查找,从节点取出数据或者索引与查找值进行比较。

​ 那么,有没有一种函数H,根据这个函数和查找关键字 Key,可以直接确定查找值所在的位置,而不需要一个个比较。这样叫“预先知道” Key 所在的位置,直接找到数据,提升效率。即:

地址index = H(key)

说白了,hash函数功能就是根据 key 计算出应该存储地址的位置,而哈希表是基于哈希函数建立的一种查找表。

2. 哈希函数的构造方法

​ 根据前人经验,统计出集中常用 hash 函数构造方法

2.1. 直接定制法

​ 哈希函数为关键字的线性函数,如 H(key) = a * key + b,这种构造方法比较简单,均匀。但是有很大限制,仅限与地址大小 = 关键字集合的情况。

2.2. 数字分析法

​ 假设关键字集合中的每个关键字 key 都是由s位数字组成(k1,k2,……,kn),分析 key 中的全体数据,并从中提取分布均匀的若干位或他们的组合构成全体。

​ 使用举例:假设要保存的集合中 key 由 5 位不同的数字构成。并且这些数字分布均匀,我们可以提取其中一位,代表数据保存的地址。H(key) = key % 100000。

​ 此种方法通常用于数字位数较长的情况,要求数字存在一定的规律,其次必须知道数字的分布情况。

2.3. 平方取中法

​ 如果关键字的每一位都有某些数字重复出现频率很高的现象,可以先求关键字的平方值,通过平方扩大差异,而后取中间数位作为最终存储地址。

​ 使用举例:

​ 比如:key = 1234 1234^2 = 1522756 取 227 作hash地址

​ 比如:key = 4321 4321^2 = 18671041 取 671 作hash地址

​ 这种方法适合事先不知道数据并且数据长度较小的情况。

2.4. 折叠法

​ 如果数字的位数很多,可以将数字分割为几个部分,取他们的叠加和作为hash地址。

​ 使用举例:

​ 比如 key=123 456 789

​ 我们可以取 1368(123+456+789=1368) 作为 hash 地址。

该方法适用于数字位数较多且事先不知道数据分布的情况。
2.5. 除留余数法(最常用)

​ H(key)=key MOD p (p<=m m为表长) 很明显,如何选取p是个关键问题。

​ 使用举例
比如我们存储3 6 9,那么p就不能取3
因为 3 MOD 3 == 6 MOD 3 == 9 MOD 3
p应为不大于m的质数或是不含20以下的质因子的合数,这样可以减少地址的重复(冲突)

​ 比如key = 7,39,18,24,33,21 时取表长m为9 p为7 那么存储如下:

Key721(冲突后移)243918(冲突后移)33(冲突后移)
2.6. 随机数法

​ H(key)= random (key) 取关键字的随机函数为它们的散列地址。

哈希函数设计的考虑因素
  • 计算散列地址所需要的时间(即 hash 函数本身不要太复杂)
  • 关键字的长度
  • 表长
  • 关键字分布是否均匀,是否有规律可循
  • 设计的 hash 函数在充分考虑了以上因素的情况下尽量减少冲突。

3. 哈希冲突

​ 上面已经提到,不同 key 通过 hash 函数产生相同的地址,即 H(key1) == H (key2),此时,key1 和 key2 就发生了 hash 冲突。

4. 哈希冲突的解决方案

​ 不管 hash 函数设计的如何巧妙,总会有特殊的 key 导致 hash 冲突, 特别是对童泰查找表来说。

4.1. 开放定制法

​ 首先有一个H(key)的哈希函数。
如果H(key1)= H(keyi),那么 keyi 存储位置 Hi = (H(key)+di) MOD m。其中 m 是表长。

di 有三种取法:

  • 线性探测再散列: di = c * i。
  • 平方探测再散列: di = i * i。
  • 随机探测再散列:di 是 一个伪随机数。
4.2. 链地址法

​ 产生hash冲突后在存储数据后面加一个指针,指向后面冲突的数据。

4.3 公共溢出区法

​ 建立一个特殊存储空间,专门存放冲突的数据。此种方法适用于数据和冲突较少的情况。

4.4. 再散列法

​ 准备若干个hash函数,如果使用第一个hash函数发生了冲突,就使用第二个hash函数,第二个也冲突,使用第三个……

5. 哈希表的查找

5.1. 哈希表查找过程

​ 查找过程和造表过程一致,假设采用开放定址法处理冲突,则查找过程为:

  • 对于给定的 key,计算 hash 地址 index = H(key)。

  • 如果数组 arr[index] == null, 则查找不成功;

  • 如果数组 arr[index] == key,则查找成功;

  • 如果 (arr[index] != null && arr[index] != key),使用 hash 冲突解决方法求下一个地址,直到(arr[index] == null || arr[index] == key)。

5.2. 哈希表的查找效率

​ 决定 hash 表查找的 ASL 因素:

  • 选用的 hash 函数
  • 选用的处理冲突的方法
  • hash 表的饱和度, 装载因子 α = n/m (n 表示实际装载数据长度, m 为表长)

01-14 09:15