7.9哈希表(散列表)

哈希表(散列表)

我们先来看看之前所学的各种查找结构的优缺点:

  • 顺序查找表:查找需要从表头开始挨个元素进行比较,速度很慢,时间复杂度为O(n)。插入删除速度很快直接插入就行了,时间复杂度为O(1)。

  • 有序查找表:由于是有序的,所以查找速度就快了很多,为O(logn)。插入删除由于需要移动记录,速度比较慢,时间复杂度为O(logn)。

  • 二叉排序树、平衡二叉树、红黑树:查找和插入删除效率都不错,为O(logn)。

我们发现,上述查找结构都不可避免要比较各个元素,有没有一种方法不需要比较,而直接通过关键字来得到查找的记录内存位置呢?这就是接下来要说的哈希表(散列表),其查找、增删元素的时间复杂度均为O(1)。

1.哈希表(散列表)的定义

可以通过某个函数f,使得:

存储位置 = f (关键字)

这样,我们可以不需要比较,直接通过这个函数来获得记录的存储位置,这就是散列技术。散列技术是在记录的位置上和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)

f称为哈希函数,或者叫散列函数采用散列技术将记录存储在一块连续的存储空间中,这块连续存储的空间称为哈希表(Hash Tabke)或散列表。

2.哈希表(散列表)的查找步骤

①哈希表的增删过程

  • 增加元素过程。 增加元素过程很简单,根据增加元素的关键字key,通过哈希函数f(key)找到将要存放的位置,再插入元素就可以,时间复杂度为O(1)。
  • 删除元素过程。 删除元素过程与增加元素过程类似,现根据要查找元素的关键字key,通过哈希函数f(key)找到要删除元素的位置,再删除元素就可以。时间复杂度为O(1)。

②哈希表的查找步骤

当查找记录时,我们同样通过哈希函数来计算记录的散列地址,按此地址访问该记录,时间复杂度为O(1)。

因此,散列技术既是一种存储方法,也是一种查找方法。

3.哈希表(散列表)的适用情况

与线性表、树等结构不同,这几种结构数据之间都存在逻辑关系。然而哈希表的各个记录之间不存在逻辑关系,只是与关键字有关联,因此哈希表是主要面向查找的数据结构。

哈希表最适合求解的问题是查找与给定值相等的记录。 如,查找一个班学号对应的人。

不适合的问题如:

  • 同样的关键字对应多个记录的情况。如,查找一个班的男生,一下子会找出很多结果,这显然不适合哈希表。
  • 范围查找的情况。 如,查找一个班18~22的同学,在哈希表中没法进行。

4.哈希冲突(哈希碰撞)

通过哈希函数计算出的key1≠key2,但是却有f(key1) = f(key2)。这种情况称之为哈希冲突key1和key2称为哈希函数的同义词

解决哈希冲突主要介绍以下两种方法:

①线性探索法(开放定址法)

线性探索法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要哈希表足够大,空的散列地址总能找到,并将记录存入。

例如:关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},哈希函数为:f(key)=key % 12.

过程如下:

  • 插入前5个数{12,67,56,16,25},没有冲突发生。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gpXGYZnC-1638341525155)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\1638339893616.png)]

  • 插入37,f(37) = 1,因此下标1后面下一个空的位置下标2处存放37。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWANihO8-1638341525157)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\1638340023639.png)]

  • 继续插入{22,29,15,47}都没有冲突。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nk6rb4a3-1638341525158)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\1638340076560.png)]

  • 插入48。f(48) = 0,与12所在的0冲突了,从0往后找1、2、3、4、5位置全冲突了,直到6才有空位置,于是在位置6存入。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8YjIffh7-1638341525159)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\1638340325390.png)]

  • 插入34。f(34) = 10,与22所在的10冲突了,于是往后找11又冲突了。这时,可以选择重头开始找,或者从10双向找空位置(二次探索法)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJ9DIqvI-1638341525160)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\1638340545735.png)]

可以看到,48和37本来都不是同义词却要争夺同一个地址的情况,我们称之为堆积。

二次探索法是先找从冲突的位置两端分别开始找空位,先找后面的一个位置,再找前面的一个位置,如此反复。这是为了防止关键字都堆积在同一个区域。

②拉链法(链地址法)

拉链法是将所有关键字的同义词存储在一个单链表中,称为同义词子表。哈希表只存放所有同义词子表的头指针。这样不论有多少冲突,都只是在当前位置给单链表增加节点的问题,但是这也带来了查找时需要遍历单链表的性能损耗

如:上述的例子用拉链法存储为:

7.9哈希表(散列表)_第1张图片

你可能感兴趣的:(数据结构,数据结构,算法,c++)