C++哈希表设计
unordered系列关联式容器
unordered_map
unordered_map的操作
unordered_map的定义

第一个模板参数是键值类型,第二个模板参数是映射值类型,这就是unordered_map的元素的键值和映射值类型,第三个模板参数是仿函数类,可以将键值计算为对应哈希值,第四个参数忽略,第五个参数空间配置器类型
empty
![]()
如果unordered_map中没有元素就返回true,如果有就返回false
size
![]()
返回unordered_map中元素个数
begin

unordered_map的迭代器其实是复用了哈希表的迭代器,返回一个哈希迭代器,其结点指针指向哈希表中第一个不为空的桶的头结点
end

返回一个unordered_map迭代器, 即哈希表迭代器,其结点指针为nullptr
operator[ ]

该函数是一个插入查询函数,返回键值k对应的映射值value的引用
其函数的行为是:(insert({k, V( )}).first)->second
如果键值为k的元素已存在,那就插入失败,返回已存在元素的迭代器和false,如果键值为k的元素不存在,那就插入成功,返回新插入元素的迭代器和true,operator[ ]使用迭代器,返回元素中映射值的引用
find

如果键值为k的元素存在,那就返回迭代器指向该元素的哈希节点,如果键值为k的元素不存在,那就返回end(),即迭代器中的哈希节点指针是nullptr
count
![]()
返回键值为k的元素的个数,因为unordered_map中不允许相同键值元素存在,所以count的返回值要么是0,要么是1
insert
![]()
val是要插入的元素,unordered_map中的元素类型是<Key, Value>,如果键值相同的元素已经存在,那就返回已存在元素的迭代器和false,插入失败,如果该键值的元素不存在,那就插入成功,返回新插入元素的迭代器和true,插入成功
erase

如果用迭代器作为参数来删除元素,那么会删除对应元素并返回删除元素下个位置的迭代器,如果删除节点后面还有下一个节点,那就返回下个节点的迭代器,如果删除节点没有下个节点,那就返回下个不为空的桶的头结点,如果后面没有不为空的桶,那就返回end()
clear
![]()
清空unordered_map中的元素
bucket_count
![]()
返回桶的数量,也就是哈希值的数量
bucket_size
![]()
返回哈希值n对应的桶里面的的元素数量,哈希值的范围是【0,bucket_count-1 】
swap
![]()
交换两个unordered_map的成员变量,即维护哈希桶的线性表和元素个数两个成员
unoedered_map模拟实现
unordered_set
unorderd_set只有底层元素和接口设计和unordered_map不一样,其他的思路都一样,unordered_set的元素是<T, T>,可以理解为<key, key>或<value, value>。同样不允许有相同键值的元素存在
底层结构
哈希概念
哈希冲突
假如两个元素的键值不相同,但是经哈希函数计算得到的哈希值相同,我们称这种情况为哈希冲突或哈希碰撞
常见哈希函数
哈希冲突解决方法
闭散列法
线性探测
从发生冲突的哈希地址开始,依次向后探测,直到寻找到下一个空位置为止。
二次探测
线性探测在冲突哈希地址的基础上每次加一个递增的偏移量,就是HashKey+i,i 取1~n,比如冲突的哈希地址为5,那下次的位置就是6、7、8....。而二次探测则是在冲突哈希地址的基础上每次加一个平方,就是 HashKey+i^2,i 取1~n,比如冲突的哈希地址为5,那下次的位置就是6、9、14......
注意:由于闭散列插入元素是当发生哈希冲突时,往冲突地址后面根据线性探测或二次探测的方法找第一个空位置然后插入,所以在进行查找时,会从哈希地址开始,然后根据插入元素时的线性探测或二次探测方法来查找元素,如果碰到空位置,说明元素不存在,如果在碰到空位置之前找到了元素,那就是存在该元素。在删除元素时,我们不能物理删除,因为这样的话会出现一个空位置,影响后面元素的查找,因此对于闭散列的哈希表实现,我们要给元素加上状态标志,分别是EMPTY、EXIST、DELETE
闭散列的哈希表实例:

依次插入1、4、5、6、7、9,都没有发生冲突,插入44,哈希值为4发生冲突,线性探测找到第一个空位置是哈希地址为8的位置,于是将44插入到8
哈希表的扩容
哈希表的载荷因子定义为元素个数和哈希地址个数的比值,当载荷因子过大时,往哈希表中插入元素时发生哈希冲突的概率就会很大,而且无论是使用闭散列还是开散列方法解决了哈希冲突,后面查找速度都会变慢,所以当载荷因子达到一个阈值时我们就对哈希表进行扩容,也就是增加哈希地址的个数
开散列法

开散列与闭散列比较
哈希表的实现
哈希的应用
位图
位图使用bit位来表示信息,常用于海量数据的场景
案例:
bitset
![]()
N是一个非类型模板参数,其值为bitset中的有效bit位数,bitset在底层开辟空间时,是一次开辟一个int也就是32个bit位,所以如果N不是32的倍数,那就会有浪费,N表示的不是开辟空间的bit位数,而是有效bit位数
bitset的操作
operator()
![]()
返回下标为pos的bit位的值,该函数没有有效字符范围检查
count
![]()
返回bitset中被设置为1的有效bit的个数
size
![]()
返回bitset中有效bit的个数
test
![]()
返回下标为pos的bit位的值,该函数和operator[ ]相比,有这个有效bit的范围检查
any
![]()
检查bitset中是否有任何一个bit位为1,如果有那就返回1,如果没有就返回0
none
![]()
检查bitset中是否所有位都没有被设置,如果都没被设置,那就返回true,否则false
set

第一个函数会将bitset中的所有有效位设置为1,第二个函数会将bitset中指定下标的bit位设置为指定值val
reset

第一个函数会将bitset中的所有位设置为0,第二个函数会将bitset中下标为pos的位设置为0
布隆过滤器
布隆过滤器是用来查找一个元素是否存在的,布隆过滤器由一个位图和多个哈希函数构成,一个元素的键值可以通过多个哈希函数计算出多个哈希值,然后设置位图中的多个不同位,基于这种特性,我们在进行查找时,如果缺少一个位没有被设置,那这个元素一定不存在,如果所有位都被设置,则元素可能存在,因为可能是由于其他元素存在导致该位被设置所以只能是可能存在
布隆过滤器的查找
布隆过滤器删除
布隆过滤器不支持删除工作,因为在删除一个元素时,可能会影响其他元素的查找。如果要支持布隆过滤器的删除功能,则每个哈希值在位图中对应的就不能是单单一个bit位,而是多个bit位,也就是该哈希值的映射计数。当我们删除一个元素时,只需要将这个元素的键值对应的哈希值对应的计数器减一即可
布隆过滤器优点
布隆过滤器缺陷
海量数据面试题
哈希切割
位图应用
更多推荐



所有评论(0)