STL(八):关联式容器

本文介绍了STL中的关联式容器,包括哈希表(如unordered_map)和红黑树结构(如map、set)。哈希表利用散列函数实现o(1)查找,通过rehash解决哈希碰撞。map和set基于红黑树,增删查改操作复杂度为o(logN),适合大数据量存储。文中还提及了SGI STL的源码学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

关联式容器与序列式容器不同,元素插入关联式容器后,插入位置是由容器采用一定的算法计算的,与插入的时间无关。本文介绍STL中哈系表、集合和map等关联式容器。

哈希表

hashtable是SGI STL中的哈希表,标准库中使用unordered_map代替hashtable,但unordered_map也是以hashtable为基础的。哈希表是一种查找操作只需要o(1)时间复杂度的数据结构,哈希表由一串连续的的bucket构成,每个bucket上挂着一个链表,链表中储存的就是用户的数据,哈系表通过散列函数将用户数据与bucket的下标对应起来,然后挂到bucket链表的尾部。哈系表的结构可以由下图表示:
在这里插入图片描述

散列函数

哈希表接受一对键值对,通过散列函数将键与bucket对应起来,hashtable和unordered_map采用除留余数法,假设当前共有N个bucket,键为M,那么该键散列后的值为 M%N,值将插入到下标为M%N的bucket上的头节点处,用户也可以自定义散列函数传入hashtable。
用户查找某个键值对时,通过散列函数找到这个键对应的bucket,然后只需要在这条链表上查找目标键值对即可。

哈希碰撞和rehash

有时会遇到很多元素都挂在同一个bucket上的情况,如下图:
在这里插入图片描述
当出现这种情况时,哈希表就退化为了链表,哈希表查找的效率就退化为了链表的查找效率o(n),这显然是不希望看到的。像这样多个数据被散列为同一个值的情况成为哈希碰撞,解决哈希碰撞的办法称为rehash。
hashtable和unordered_map都提供了一个升序的质数表,并使用一个pos指针指向当前使用的质数,rehash时,将pos指向质数表中下一个质数,使用新的质数作为除留余数法的除数,显然bucket的个数与除数的大小是一致的,对原本储存的所有元素重新计算散列值,并挂到相应的bucket上,这样就减少了哈希碰撞。
那么什么时候应该进行rehash呢?unordered_map使用负载因子,负载因子的计算方法为元素的总量/bucket的数量,当负载因子超过1时,unordered_map就进行rehash,可以通过下面的程序验证这个过程:

int main(){
    std::unordered_map<int, int> mp;
    for(int i=0;i<15;++i){
        mp.insert(std::make_pair(i,i));
        std::cout << "插入第" << i <<"个键值对:" << std::endl;
        std::cout << "负载因子为:" << mp.load_factor() << std::endl;
        std::cout << "质数为::" << mp.bucket_count() << std::endl;
    }
    return 0;
}

运行结果如下:
在这里插入图片描述

_Hashtable_iterator

hashtable提供forward_iterator,_Hashtable将从第一个bucket的链表开始,遍历,遍历到链表的末尾时,则跳到下一个bucket的链表上,当然这通过重载operator++来实现,用户是无感知的。由于vector上存储的都是哑节点,因此所有数据都被存储在链表中,迭代器失效规则也与链表相同,仅删除数据会导致被删除节点的iterator失效。

hashmap和hashset

这两种适配器由SGI STL提供,并未归入标准库中,本质上仍旧是哈希表,且使用场景也较少,这里就不介绍这两中适配器,有兴趣可以自行下载源码查看。

map和set

map和set底层都是红黑树结构,map和set的操作与红黑树完全相同,仅是对红黑树操作的封装。
map接受一对键值对,红黑树按键的大小确定键值对的存放位置,这听起来与哈希表很相似,都是通过键值来决定存放位置,加快查询效率,但不同的是,map的增删查改的复杂度都是o(logN)级别的,而哈希表的增删查改都是o(1)级别的;哈希表的由于负载因子的控制,会有很多储存空间闲置,当储存的数据量很大时,这种空间的使用就显得非常浪费,而红黑树使用的空间数量和数据数量相同,因此如果数据量很大时,通常使用红黑树存储,而数据量较小时,可以发挥哈希表的优势,进行快速的增删查改。
set是STL中的集合,在红黑树中,是一种键和值相等的键值对,因此只需要传入一个参数即可。
multimap和multiset是几乎和map与set完全相同的适配器,唯一的不同点是multimap和multiset允许元素重复,而map和set不允许,这是因为STL的红黑树提供了insert_unique和insert_equal两种插入方式,map和set的插入使用的是insert_unqie方式,这使得插入元素的值已存在时会被丢弃,而multimap和multiset使用的insert_equal则不会。

总结

本文我们介绍了STL中的关联式容器及适配器,至此,我们已经介绍了STL中所有的容器及适配器,STL是数据结构和算法的集大成者,STL的容器和适配器几乎涵盖了所有常见的数据结构(除了图),值得我们反复学习推敲。

SGI STL源码

关联式容器源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值