STL源码剖析(5)关联容器
在STL中,关联容器核心的只有两个:set和map,与之相关的还有multi和unordered。组合起来一共就是8个
底层实现
STL中,关联容器的实现有两个数据结构,红黑树和hash表。
红黑树
红黑树是一种平衡二叉树,他有这些特点
- 树上所有的节点都带有颜色,要么红色要么黑色。
- 根节点是黑色
- 根节点到所有叶子节点路径上的黑节点个数相同
- 没有两个红色节点相连,换句话说,红节点的儿子和父亲都必然是黑结点
这里只谈插入。
根据规则3,在一棵红黑树中插入黑节点,必然导致3失效。所以插入的新节点只能是红节点。
但是插入红节点又可能导致规则4被破坏,那么这个时候就需要调整树的结构,重新染色了。
怎么调,怎么染色,需要看三层。第一层是插入的那一层,第二层是父节点那层,这一层其实要看两节点,父节点和伯父节点;第三次是祖父节点。
首先有一个很简单的道理,就是如果我们需要调整,那父节点必然是红节点,否则直接插入就好了。那祖父节点必然是黑节点,因为规则4,
然后就是4种插入的情况了,这个跟AVL的一样,就是单旋和双旋。无非就是多了个染色,染色画个图自己染一染也看得出来。
- 伯父节点是黑色,左左,右右,单旋,染色
- 伯父节点是黑色,左右,右左,双旋,染色
- 伯父节点是红色,左左,右右,单旋,染色
- 伯父节点是红色,左右,右左,双旋,染色
STL中红黑树的设计
节点设计成两层,第一层保存了:颜色,指向父节点的指针,左儿子,右儿子,第二层继承于第一层,额外保存了:值。
迭代器的设计也分成了两层:有一层基类迭代器,保存了一个指向红黑树节点的指针。第二层继承于第一层,实现迭代器的一些操作。比如++,--之类的。
红黑树的++操作。这里我们认为是中序遍历的下一个值。做法是这样:
- 当前指针的左子树必然已经遍历过了,因为中序遍历是左中右,到了中,那么左肯定遍历了。下一步就是找右,所以是先走一步右儿子,然后左走到底(中序遍历很好理解),如果右儿子是叶子,那就直接返回就行了。
- 如果没有右儿子,说明当前节点的子树已经搞完了,需要回溯了。回溯是需要当前是哪棵子树,如果是左子树遍历完了需要回溯,下一步就是遍历右子树;如果是右子树遍历完了,就要继续回溯。判断方法是:如果父节点是个左儿子,说明当前的是左,下一步是访问中。如果父节点是个右儿子,那说明是右子树,需要继续回溯
- 对于回溯来说,我们一直要找到一个不是右儿子的父节点,然后遍历他,然后遍历他的右儿子。
红黑树的--操作是类似的
底层结构:与普通的树的结构一样,只是在根节点上多了一个父节点。就像链表的空头节点一样。
插入:STL提供了两种插入,insert_equal()和insert_unique(),前者允许重复元素,后者不允许。
hash表
哈希函数
哈希函数就是把键映射到一个index上的函数。因为任何类型的底层都是int,double,char之类的基本类型,所以这个键可以是任何类型。
哈希冲突
负载系数,哈希表实际存储量与总容量的比值。
- 线性探测:顺序寻找下一个位置。
- 二次探测:下一个位置不再是+i,而是加i^2。如果哈希表的总量是质数,且负载系数在0.5一下,那么每次插入一个元素,其探测次数不会超过2.
- 开散列,每个元素值后面搞一个链表,连着相同哈希值的元素。
STL中是以开散列的方式处理哈希冲突的。每个元素属于一个hashtable_node,这个node同时又是一个链表的node,保存了元素值和next指针。具有相同hash值的元素被那个位置的链表链起来。
哈希数组用vector保存,也就是一个vector<hash_node>这样子。
set,multiset,
set是集合,multiset是元素可重复的集合。set和multiset就是对红黑树做了一层接口的包装。本质上就是个红黑树。区别是:set在插入的时候用的是insert_unique,multiset用的是insert_equal
map,multimap,
map就是把红黑树的value换成了pair,其中键的类型是const的,表示键是不能被修改的。其他的跟set一样
set和map都有一个模板参数,Comp,这是个仿函数,用来指定Value是如何进行比较的。
unordered系列
这个系列的容器有
- unordered_set
- unordered_multiset
- unordered_map
- unordered_multimap
接口什么的都与那啥一样,就是底层用的是哈希表。
因为底层不需要排序,所以他们是没有Comp的,但是需要指定哈希函数。默认的哈希函数是标准库中的hash
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!