哈希表结构_基础理论知识
基础理论知识
1. 哈希表的定义
1. 哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过关键码映射的位置查找存放值的位置。
2. 哈希表的结构其实就是数组,但神奇的地方是对下标值的转换,这种转换我们称之为哈希函数,
通过哈希函数可以获取到HashCode。
2. 哈希表的重要性
哈希表是一种重要的数据结构,几乎所有的编程语言都直接或者间接的用到哈希表结构
3. 哈希表的优点
哈希表是基于数组实现的,但是相比于数组,又有很多的优势
这些优势包括:
1. 哈希表在实现 插入-删除-查找 的操作时,效率更高
2. 无论存储了多少数据,插入和删除的实际复杂度都接近于常数(即O(1))
实际上只需要几个机器指令即可
3. 哈希表的速度比树还要快,基本是瞬间可以查找到指定元素
4. 哈希表相对于树来说,编码更简单
4. 哈希表的缺点
1. 哈希表中的数据是无序的,不能以一种固定的方式(如从小到大)来便利其中的元素
2. 通常情况下,哈希表中的key是不允许重复的,不能用相同的key保存不同的元素
5. 将字符串转换成数字的方案
哈希表的实现,需要字母的编码,编码方式有很多,常见的是utf-8
1. 字母对应数字的累加(数组下包括太小,容易出现重复)
2. 幂的连乘(产生的数组下标太大,占用内存空间太多)
3. 哈希化
6. 哈希表相关的概念
1. 哈希化:将大数字转化成数组范围内下标的过程恒指为哈希化
2. 哈希函数:通常我们将单词转化成大数字,大数字在进行哈希化的代码放在一个函数中,
这个函数就称之为哈希函数
3. 哈希表:最终将数据哈入到这个数组,对整个结构的封装,就称之为哈希表。
7. 哈希表存在下标值重复的问题
案例:
想将英文单词全部映射到一个表中,通过下标来索引。
实现:
1. 通常单词长度不超过10,因此单词从:aaaaaaaaaa --> zzzzzzzzzz
2. 通过幂的连乘方式进行编码,产生下标从:1111111111 --> 28888888886
幂的连乘:
cat: 3*10^2 + 1*10^1 + 20
3. 这个下标空间就太大
4. 因此我们使用取余操作来压缩空间,即将所有数除以10(也可以是别的数)取余。
但研所后还是存在不同单词计算出来的下标是一样的情况(即下标重复)
8. 解决哈希表下标重复的问题
两种解决方案:
1. 链地址法(也叫拉链法)
1. 每个数据单元存储的不是单个数据,而是一个数组或者链表
2. 比如是链表,每个数据单元存储着一个链表,一旦发现重复,就将重复的元素插入道链表的首部或者尾部
3. 当查询时,先根据哈希化的下标值找到响应的数据单元位置,在取出链表,依次查找寻找的数据
2. 开放地址法
工作方式,就是寻找空白的单元格来添加重复的数据
寻找空白单元格的方法:
1. 线性探测:从查找到的位置,依次往后查找
注意点:
1. 插入时,本应插入的位置若不为空,则往后依次查询,直到找到空位置
2. 提取数据时,从本应提取的位置找,如没找到,依次往后找,遇到空位置则停止查找
3. 删除数据时,被删除位置应设置为一个特殊的值(如-1),
这样能保证提取数据时不发生错误
存在的问题:聚集
即在一段空间中,连续的存储单元均被放置了元素,这样就使得插入,提
取和删除数据时,每次都要经过长时间的查找,严重影响插入/查询/删除操作的性能
2. 二次探测:
1. 二次探测可以尝试解决线性探测中存在的聚集问题
2. 二次探测主要的优化就是探测时的步长
3. 线性探测是,步长恒为1,从下标x开始,依次探测x+1, x+2, x+3......
4. 二次探测时,步长优化了,从下标x开始,依次探测x+1^2, x+2^2, x+3^2......
这样一次性探测很长的距离,就可以避免聚集带来的问题
存在的问题:
连续插入同一个位置的数据时,由于累加后的步长相同,会造成远距离位置的聚集
3. 再哈希法
1. 再哈希法还是用来解决聚集问题的
2. 设计一种计算方法,产生一种依赖于关键字的探测序列
3. 不同的关键字输入进来时,虽然本应查找的位置相同,但是由于关键字不同
再哈希时,会产生不同的探测序列(即探测步长不一样),而消除聚集的问题
4. 再哈希法的特点就是探测步长依赖于关键字,用另一个哈希函数对关键字再做一次哈希化
哈希化的结果作为步长
第二次哈稀的特点:
1. 所使用的的哈希函数与第一次不同
2. 哈希结果部位0,不然不会向下查找
再哈希函数:
计算机专家已经设计出了一个工作很好的哈希函数:
setpSize = constant - (key % constant)
其中,
constant是一个常数,并且是一个质数,并且小于数组容量
哈希化的效率:
1. 如果没有产生冲突(每次查找到的位置都是空位置,不需要往下查找),那么效率会很高
2. 如果发生冲突,存取事件就依赖于后来探测的长度
3. 平均探测长度以及存取时间,取决于填装因子(loader factor),随着填装因子的增大,探测长度会变长
4. 随着填装因子的增大,效率会下降,但是开放地址法比链地址法下降的更快
注:填装因子 = 已填数据项 / 哈希表长度
实际开发时,倾向于用链地址法更多一些
9. 哈希函数说明
1. 一个好的哈希函数,应该尽可能规定额让计算变得简单,计算效率提高
哈希表的主要有点是速度快,如果速度上满足不了要求,就达不到设计的初衷
提高计算速度的一个方法就是在哈希函数中,尽量少用乘法和除法,因为他们的性能是比较低的
2. 一个好的哈希函数应具备的优点
1. 快速的计算
哈希表的优势就是能快速获取到HashCode,
2. 均匀的分布
优秀的哈希函数可以将映射到同一个位置的元素均匀的在哈希表中分布
10. 优秀的哈希函数使用到的手段
1. 快速计算:霍纳法则
在之前的哈希值计算时,我们用多项式法:
如,cats = 3 * 10^3 + 1 * 10^2 + 20 * 10^1 + 17
这种多项式计算法可以归纳为:
P(n) = a(n)*x^n + a(n-1)*x^(n-1) + a(n-2)*x^(n-2) + ...... + a(1)*x + a(0)
这种计算是,使用了
乘法:n + (n-1) + (n-2) + ... + 1 = n(n-1)/2 次
加法: n次
上述计算可以转换方法: 霍纳法则(在中国也叫秦九韶算法)
P(n) = a(n)*x^n + a(n-1)*x^(n-1) + a(n-2)*x^(n-2) + ...... + a(1)*x + a(0)
= ((...(((an*x + an-1)x + an-2)x + an-3)...)x + a1)x + a0
转换后,使用了
乘法: n次
加法: n次
2. 均匀分布
1. 设法处理不同元素映射到相同位置时的情况:链地址法/开放地址法
2. 在需要使用常数的地方选择使用质数