关联式容器
底层结构有两种—红黑树和哈希(哈希后面讲)
map,set,multimap,multiset
相同点:
- 底层结构相同—红黑树,红黑树是二叉搜索树+条件限制
- 查询时间复杂度是O(logN)
- 使用迭代器遍历(中序遍历规则)都会得到一个关于key的有序序列
- 默认按照less方式比较–升序序列
- 所有key不能修改—如果修改,必须先删除后插入
- 查找规则相同:按照二叉搜索树的规则进行查找
不同点:
- 存储元素类型不同
map:kv模型的键值对,key唯一
set:k模型—key就是val,val就是key
multimap:kv模型的键值对,key可以重复
multiset:
map
multimap没有[]操作,因为有多个key,不知道返回谁的val
set
功能:去重+排序
向set中插入很多数据后,set会自动去除
所有multiset功能就剩排序了
哈希
在查找元素时不比较元素
- 方法:在存储元素时,通过某种机制让元素与其存储位置建立一一对应的关系,则查找时几乎就不需要比较了
- 插入:通过函数(数学中的函数式)计算数据在表格中的位置
再插入
函数就称为哈希函数,表称为哈希表
缺陷:不同的元素通过相同的哈希函数,算出了相同的哈希地址
常见函数
直接定值:关于元素k的一元一次函数
除留取余: Hash(key) = key% p(p<=m), p最好是一个不超m的最大的一个素数
真正解决哈希冲突的方式
闭散列
从哈希冲突的位置开始找“下一个非空的位置
线性探测:从发生冲突的位置依次向后找,如果查找到空间末尾,也没有发现空位置,再折回到空间起始位置继续查找,直到找到空位置
- 问题:
如何知道这个位置上有没有元素- 解决:
给每个空间一个标记:EMPTY,EXIST,DELETE- 查找:
通过哈希函数计算元素在表中的位置,检测位置是否是空- 插入:
通过哈希函数计算插入的位置,检测该位置是否发生冲突
没有发生插入,并修改状态
发生冲突,找到下一个空位置插入- 删除:
通过哈希函数计算删除的位置,检测位置是否为空
空则元素不存在
非空检测是否是待删除元素(删除就修改状态为DELETE,不能直接改成空)
线性探测的缺陷:容易产生数据堆积—发生冲突,冲突的元素容易连成一片
二次探测:H(i)=(H(0) + i^2)%表格容量
i表示探测次数
- 插入
计算哈希地址
检测位置是否为空
空—直接插入—修改状态为EXIST
非空–发生冲突—二次探测
二次探测缺陷:表格中空位置少的时候,会找很多次
解决方式:达到一定程度就进行扩容
这个程度就的负载因子=元素个数/表格容量
线性探测控制到70%
二次探测控制到50-60%
空间换时间的方法
开散列–链地址法
将发生冲突的元素用单链表链接起来
实质是数组+链表的集合
每个链表称为桶
冲突元素多了,哈希桶就退化成链表了
最理想的状态是哈希桶中,桶的个数和元素的个数相等,也就是每个数组元素下只有一个链表
扩容之后哈希桶容量改变了,哈希函数就变了
如果一个桶中出现很多节点,就要将其转换成红黑树,不然查找效率太低了