Skip to content

Latest commit

 

History

History
109 lines (69 loc) · 5.77 KB

数据结构-哈希表.md

File metadata and controls

109 lines (69 loc) · 5.77 KB

哈希表概念

基本概念

  • HashMap 是一个散列桶(数组和链表),它存储的内容是键值对 key-value 映射
  • HashMap 采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改
  • 查找效率主要取决于散列函数、处理冲突的方法和装载因子

散列函数

  1. 直接寻址法 取关键字或关键字的某个线性函数值为散列地址。即 H(key)=key 或 H(key) = a·key + b,其中 a 和 b 为常数(这种散列函数叫做自身函数)。若其中 H(key)中已经有值了,就往下一个找,直到 H(key)中没有值了,就放进去。
  2. 数字分析法 分析一组数据,比如一组员工的出生年月日,这时我们发现出生年月日的前几位数字大体相同,这样的话,出现冲突的几率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果用后面的数字来构成散列地址,则冲突的几率会明显降低。因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。
  3. 平方取中法 当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。
  4. 折叠法 将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址。数位叠加可以有移位叠加和间界叠加两种方法。移位叠加是将分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加。
  5. 随机数法 选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合。
  6. 除留余数法 取关键字被某个不大于散列表表长 m 的数 p 除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对 p 的选择很重要,一般取素数或 m,若 p 选的不好,容易产生同义词。
  7. 乘法取整法 f(x):=trunc((x/maxX)*maxlongit) mod maxM,主要用于实数。

处理冲突,解决 hash 碰撞

开放定址法

开放定址法的公式为

Hi = (H(key) + di) MOD m,i=1,2,...k(k<=m-1)

其中 H(key)为散列函数,m 为散列表长,di 为增量序列,H(key)可有下列三种取法:

线性探查法

di = 1,2,3,...,m-1

探查过程终止于三种情况:

  1. 若当前探查的单元为空,则表示查找失败(若是插入则将 key 写入其中)
  2. 若当前探查的单元中含有 key,则查找成功,但对于插入意味着失败
  3. 若探查到 T[d-1] 时仍未发现空单元也未找到 key,则无论是查找还是插入均意味着失败(此时表满)

线性探查法缺点有如下几点:

  1. 处理溢出需另编程序。一般可另外设立一个溢出表,专门用来存放上述哈希表中放不下的记录。此溢出表最简单的结构是顺序表,查找方法可用顺序查找。
  2. 按上述算法建立起来的哈希表,删除工作非常困难。假如要从哈希表中删除一个记录,按理应将这个记录所在位置置为空,但我们不能这样做,而只能标上已被删除的标记,否则,将会影响以后的查找。
  3. 线性探测法很容易产生堆聚现象。所谓堆聚现象,就是存入哈希表的记录在表中连成一片。按照线性探测法处理冲突,如果生成哈希地址的连续序列愈长 ( 即不同关键字值的哈希地址相邻在一起愈长 ) ,则当新的记录加入该表时,与这个序列发生冲突的可能性愈大。因此,哈希地址的较长连续序列比较短连续序列生长得快,这就意味着,一旦出现堆聚 ( 伴随着冲突 ) ,就将引起进一步的堆聚

二次探查法

di = 1^2,-1^2,2^2,-2^2,3^2,-3^2,...,+-(k)^2,(k<=m/2)

伪随机探测再散列

di = 伪随机数序列

再散列法

Hi = RHi(key),i=1,2,...,k 

RHi 均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。

链地址法(拉链法)

下图为拉链法的示意图:

拉链法

看过 jdk hashmap 源码的同学都知道,jdk 也是通过拉链法来解决冲突的。

jdk 中,调用键对象的 hashCode() 方法来计算 hashCode,当遇到 hashCode 重复时,就在开辟一个空间用来储存,并将空间链接到这个 hashCode 对应的链下。从而解决冲突问题。

建立一个公共溢出区

这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

相关题目

简单题型

中等题型

困难题型

参考资料

思维导图

数据结构-哈希表-思维导图.png