序言
上次美团电话面试,面试官问了解hash表吗?可惜平时没有好好看过hash表的东西,根本回答不上来。
准备学习一下Hash表的概念和实现。
哈希表-Hash Table又称散列表
是根据关键码值而直接进行访问的数据结构。
散列表算法在查找时不需要进行一系列和关键字(关键字是数据元素中某个数据项的值,用以标识一个数据元素)的比较操作。
散列表算法希望能尽量做到不经过任何比较,通过一次存取就能得到所查找的数据元素,因而必须要在数据元素的存储位置和它的关键字(可用key表示)之间建立一个确定的对应关系,使每个关键字和散列表中一个唯一的存储位置相对应。因此在查找时,只要根据这个对应关系找到给定关键字在散列表中的位置即可。这种对应关系被称为散列函数(可用h(key)表示)。
可将其抽象为“数组 + 链表”的复合结构。
哈希表的实现
有多种实现方法,最常用的是拉链法-链表的数组
建立一个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去。也是根据这些特征,找到正确的链表,再从链表中找出这个元素
数组和链表的特点
- 数组:寻址容易,插入和删除困难
- 链表:插入和删除容易,寻址困难
哈希表为什么能提高查找速度
- [1] 哈希函数建立了数据存储位置和数据记录的内容的关联
- [2] 采用了数组“快速访问”的特点,利用哈希函数对关键码值进行转换并快速定位,不需要像链表那样从头节点开始一个个遍历去查
哈希函数-Hash Function,又名散列函数,一般来说散列函数满足以下两个条件+两个附加条件
[1] 对输入值运算,得到一个固定长度的摘要(Hash value)
[2] 不同的输入值可能对应不同的输出值
(1) 散列函数的输出值尽量接近均匀分布
(2) x的微小变化可以使f(x)发生非常大的变化,即所谓“雪崩效应”
常用的Hash函数构造方法
直接定址法:
- 取关键字或关键字的某个线性函数值为散列地址,即: h(key) = key 或 h(key) = a * key + b,其中a和b为常数。
数字分析法
平方取值法
- 取关键字平方后的中间几位为散列地址:如index = (key * key) >> 28
- 折叠法
- 将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为散列地址。
- 除留余数法
- 取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址:index = key % 16
- h(key) = key MOD p p ≤ m
- 随机数法
- 选择一个随机函数,取关键字的随机函数值为它的散列地址,即:h(key) = random(key),其中random为随机函数。
比较常用的是:除留余数法 + 平方散列法 + Fabonacci散列法(平方散列法的改进)
Fabonacci散列法:index = (key * 2654435769) >> 28
[1] 对于16位整数而言,这个乘数是40503
[2] 对于32位整数而言,这个乘数是2654435769
[3] 对于64位整数而言,这个乘数是11400714819323198485哈希冲突
- 哈希函数是一个多对一函数,如果记录Hash值的范围多于Hash表的条数,不可避免的会发生冲突
哈希冲突的解决方案
[1] 对Hash表中每个Hash值建立一个冲突表,即将冲突的几个记录以表的形式存储在其中;
[2] 改变规则重新计算一次Hash值;
[3] 建立一个公用区域存放冲突的表项;
在工程上,考虑到实现算法的复杂度,方法1用得是最多的
Hash的基本思路说明
现有4个元素要存储 13 7 14 11
用数组来存储是: a[0] = 13, a[1] = 7, a[2] = 14, a[3] = 11
Hash存储过程如下:
1. 先确定Hash函数,假如使用 h(key) = key % 5
2. 映射并存储
四个元素分别得到 h(13) = 3, h(7) = 2, h(14) = 4, h(11) = 1,
此时对应存储为 Hash[3] = 13, Hash[2] = 7, Hash[4] = 14, Hash[1] = 11
3. Hash查找
对于数组,要找一个元素,顺序查找一遍对比是否是要找的元素,比如查找11,需要查找4次
对于Hash表,先做hash映射h(11) = 1,然后查看Hash[1] ?= 11即可,只找了一次
Acknowledgements:
http://blog.csdn.net/u013630349/article/details/47856815
http://blog.csdn.net/songzi1111/article/details/10985429
http://blog.csdn.net/v_JULY_v/article/details/6256463
2017.09.20