目录
什么是散列表(哈希表)?
和二叉树、链表这一类一样。它是一种数据结构,设计出来用于存放数据。
数组: | 指针 | 指针 | 指针 | 指针 | 指针 | |||||
下标: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | …… |
为什么叫哈希表呢?
因为里面有一个哈希函数
关键字 : x ——> f(x) (哈希函数) ——>下标
哈希函数是根据关键字设计的,有很多种函数,主要的原理就是根据数组的大小求模运算。
(关键字) % (数组大小)
例如:20048157%17 (结果在0-16)
数组的大小一般设计为质数,因为需要均匀的散布
遇到冲突怎么办?
1、链表式解决
写结构体的时候加入next指针(和链表一样)
数据 | Next | ——> | 数据 | Next |
遇到冲突的时候,把数据写到next的位置
举个例子
下标 | 数据 | ||
0 | |||
1 | 15 | ——> | 22 |
2 | 16 | ||
3 | 24 | ||
4 | |||
5 | |||
6 |
数据关键字:15、16、22、24
数组大小:7
哈希函数:下标 = 关键字 mod 7
这里怎么填写呢?
15除以7余1,所以填在下标为1的地方,16除以7余2,所以填在下标为2的地方,22除以7余,所以填在下标为1的地方,出现了冲突,用指针指出,24除以7余3,所以填在下标为3的地方
2、开放地址
不用next指针,把其他下标的位置都对外开放
开放地址的方法:
a.线性探测法
b.平方探测(二次方探测)
c.双哈希
2.1线性探测法
如果遇到冲突,就往下一个地址寻找空位
如果遇到冲突,新位置 = 原始位置 + i (i 是查找的次数)
数组: | 12 | 15 | 2 | 28 | 4 | 38 | |||||||
下标: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
数据关键字:15、2、38、28、4、12
数组大小:13
哈希函数:下标=关键字 mod13
15除以13余2,放到2下标位置,2除以13余2,2下标被占了,向下一个3下标,38除以13余12,放到12下标,28除以13余2,放到2下标,2下标和三下标都被占了,放到4下标,4除以13余4,4下标被占了,放到5下标,12除以13余12,12下标被占了,循环放到0下标
2.2平方探测法
如果遇到冲突,就往(原始位置+i^2)的位置寻找空位。(i代表查找的次数)
如果遇到冲突,新位置=原始位置 + i^2
数组: | 15 | 2 | 28 | 19 | 10 | ||||||||
下标: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
数据关键字:15、2、28、19、10
数组大小:13
哈希函数:下标=关键字 mod13
15除以13余2,放到2下标,2除以13余2,放到2下标被占,1^2向后挪,放到3下标,28除以13余2,2下标被占,1^2向后挪,3下标被占,2^2向后挪,放到6下标,19除以13余6,6下标被占,向后1^2挪放到7下标,10除以13余10,放到10下标
2.3 双哈希
要设置第二个哈希函数
例如;hash2(key)=R-(key mod R)
R要取比数组尺寸小的质数
例如R=7:hash2(关键字)=7-(关键字%7)
也就是说,二次哈希的结果在1-7之间,不会等于0;
如果遇到冲突,新位置 = 原始位置 + i ·hash2(关键字)
数组: | 15 | 18 | 2 | 28 | |||||||||
下标: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
数据关键字:15、2、18 、28
数组大小:13
哈希函数:下标=关键字 mod13
哈希函数2:7 - (关键字 mod 7)
如果遇到冲突,新位置=原始位置+i·hash2(关键字)
15除以13余2,放到2下标,2除以13余2,出现冲突,代入哈希2里,得到5,向后查5格,放到7下标,18除以13余5,放到5下标,28除以13余2,2下标被占,代入哈2,得到7,向后查7格,放到9下标
哈希表满了怎么办?
再次哈希
当哈希表数据存储量超过70%,那么就自动新建一个新的哈希表
新表的尺寸是旧表的2倍以上,选择一个质数
把之前的数据再次通过哈希计算搬到新表里
为什么设计哈希表?
因为哈希表查找的性能快,它比搜索二叉树的速度还快。
搜索二叉树的查找速度是O(log2N),而哈希表发挥稳定的话可以达到O(1)。