1.hashmap查插删改的时间复杂度
是常数,最坏的情况(键值全部冲突)复杂度上升为O(n)
2.哈希函数
一般的说,Hash函数可以划分为如下几类:
1. 加法Hash
2. 位运算Hash
3. 乘法Hash
4. 除法Hash
5. 查表Hash
6. 混合Hash
7.数组Hash
3.哈希冲突的解决办法
一般比较常用的方法有开放地址法:(内容来自百度百科)
1. 开放寻址法:Hi=(H(key) + di) MOD m,i=1,2,…,k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
1.1. di=1,2,3,…,m-1,称线性探测再散列;顺序查看表的下一单元,直至找到某个空单元,或查遍全表。
1.2. di=1^2,-1^2,2^2,-2^2,⑶^2,…,±(k)^2,(k<=m/2)称二次探测再散列;在表的左右进行跳跃式探测。
1.3. di=伪随机数序列,称伪随机探测再散列。根据产生的随机数进行探测。
2 再散列法:建立多个hash函数,若是当发生hash冲突的时候,使用下一个hash函数,直到找到可以存放元素的位置。
3 拉链法(链地址法):就是在冲突的位置上简历一个链表,然后将冲突的元素插入到链表尾端,
4 建立公共溢出区:将哈希表分为基本表和溢出表,将与基本表发生冲突的元素放入溢出表中。
4.hashmap的底层实现(stl)
数组+链表(拉链法)
使用一个下标范围比较大的数组来存储元素(桶)
插入过程
- 得到key
- 通过hash函数得到hash值
- 得到桶号(一般都为hash值对桶数求模)
- 存放key和value在桶内
其取值过程是:
- 得到key
- 通过hash函数得到hash值
- 得到桶号(一般都为hash值对桶数求模)
- 比较桶的内部元素是否与key相等,若都不相等,则没有找到。
- 取出相等的记录的value。
stl的实现方法是数组存桶,桶中是一个链表来存在具体的数据
也就是说一个桶中可能有多个hash值相同的数据(key-value)
因此取数据时需要用到比较函数
5.装载因子
当前元素数量除以桶的数量
6.rehash
当装载因子>=1时(默认),认为当前hashmap需要扩容来减少冲突
因此会增加一些桶数(类似vector扩容)
重新计算出的桶数是比当前桶数大的下一个质数
rehash的过程会重新填装原有的元素
7.素数
用素数作为桶号可以在一定程度上减少冲突
在哈希函数计算桶号时会使用求余计算,假设桶数是2次幂比如4和8
那么10%4与10%8结果一致
如果桶数永远是素数,那么rehash时无论什么冲突解决策略下都可以最大可能的减少冲突
8.什么时候用hashmap什么时候用map
hashmap的总体平均速度要快于map,但是不如map稳定
map使用红黑树(二叉查找树)存放数据,速度是稳定的log2 N
查找速度, 数据量, 内存使用
hashmap平均速度快,map稳定
hashmap适合大数据量,但内存占用更多
参考文献
https://zhuanlan.zhihu.com/p/88319168
https://blog.csdn.net/fsfsfsdfsdfdr/article/details/82697289