STL——关联式容器
一、关联式容器
标准的STL关联式容器分为set(集合)/map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和 multimap(多键映射表)。这些容器的底层机制均以RB-tree(红黑树)完成。RB-tree也是一个独立容器,但并不开放给外界使用。
此外,SGI STL 还提供了一个不在标准规格之列的关联式容器:hash table (散列表),以及以此hash table 为底层机制而完成的hash_set(散列集合)、hash_map(散列映射表)、hash_multiset(散列多键集合)、hash_multimap(散列多键映射表)。
所谓的关联式容器,观念上类似关联式数据库:每笔数据(每个元素)都有一个键值(key)和一个实值(value)。当元素被插入到关联式容器中时,容器内部结构(可能是RB-tree ,也可能是hash-table)便依照其键值大小,以某种特定规则将这个元素放置于适当位置。关联式容器没有所谓的头尾(只有最大元素和最小元素),所以不会有所谓push_back()/push_front()/pop_back()/pop_front()/begin()/end() 这样的操作行为。
一般而言,关联式容器的内部结构是一个balanced binary tree(平衡二叉树),以便获得良好的搜寻效率。balanced binary tree 有许多种类型,包括AVL-tree、RB-tree、AA-tree ,其中最被广泛运用于STL的是RB-tree(红黑树)。
二、树
有关树以及红黑树的相关内容,参见 红黑树详解 博文。 红黑树及其迭代器源码请参考《STL源码剖析》5.2节。
三、set
set 的特性是,所有元素都会根据元素的键值自动被排序。set 的元素不像map那样可以同时拥有实值和键值,set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。
务必注意,我们不能通过set的迭代器改变set的元素值。因为set元素值就是其键值,关系到set元素的排序规则。如果任意改变set元素值,会严重破坏set组织。在set的源码中,set<T>::iterator被定义为底层RB-tree 的const_iterator,杜绝写入操作。换句话说,set iterators 是一种constant iterators(相对于mutable iterators)。
STL特别提供了一组set、multiset 相关算法,包括交集set_intersection、并集set_union、差集set_difference、对称差集set_symmetric_difference。
由于RB-tree 是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL set即以RB-tree为底层机制。又由于set 所开放的各种操作接口,RB-tree也都提供了,所以几乎所有的set操作行为,都只是转调用RB-tree 的操作行为而已。
参见相关源码;
四、map
map的特性是,所有元素都会根据元素的键值自动被排序。map的所有元素都是pair,同时拥有实值和键值。pair 的第一元素被视为键值,第二元素被视为实值。map不允许两个元素拥有相同的键值(但两个元素的实值可以相同)。同样,我们不能通过map的迭代器改变map的键值,但我们可以改变其实值。因此,map iterators 既不是一种constant iterators,也不是一种mutable iterators。map也是也是以RB-tree为底层机制,通过转调RB-tree的操作行为来实现自己的接口功能。
参见相关源码;
五、multiset
multiset 的特性以及用法和set 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制RB-tree的insert_equal()而非insert_unique()——insert_equal()表示被插入节点的键值在整棵树中可以重复,因此,无论如何插入都会成功(除非空间不足导致配置失败)。insert_unique()表示被插入节点的键值key在整棵树中必须独一无二(因此,如果树中已存在相同的键值,插入操作就不会真正进行)。
参见相关源码;
六、multimap
multimap 的特性以及用法与map完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制RB-tree的insert_equal()而非insert_unique()。 参见相关源码;
参见附录1 SGI STL RB-tree的insert_equal()和insert_unique()插入源码
七、hashtable
参见 hashtable详解
八、hash_set
虽然STL 只规范复杂度与接口,并不规范实现方法,但STL set 多半以RB-tree 为底层机制。SGI 则是在STL 标准规格之外另又提供了一个所谓的hash_set,以hashtable 为底层机制。由于hash_set所供应的操作接口,hashtable都提供了,所以几乎所有的hash_set 操作行为,都只是转调用hashtable的操作行为而已。
运用set,为的是能够快速搜寻元素。这一点,不论其底层是RB-tree 或是hashtable,都可以达成任务。但是请注意,RB-tree 有自动排序功能而hashtable 没有,反映出来的结果就是,set的元素有自动排序功能而hash_set 没有。 hash_set 的使用方式,与set完全相同。
参见相关源码;
九、hash_map
SGI 在STL 标准规格之外,另提供了一个所谓的hash_map,以hashtable 为底层机制。由于hash_map 所供应的操作接口,hashtable 都提供了,所以几乎所有的hash_map操作行为,都只是转调用hashtable 的操作行为而已。
RB-tree 有自动排序功能而hashtable 没有,所以map 的元素有自动排序功能而hash_map没有。
参见相关源码;
十、hash_multiset
hash_multiset 的特性于multiset 完全相同。唯一的差别在于它的底层机制是hashtable。也因此,hash_multiset 的元素并不会被自动排序。
hash_multiset 和 hash_set 实现上的唯一差别在于,前者的元素插入操作采用底层机制hashtable的insert_equal(),后者则是采用 insert_unique()。
十一、hash_multimap
hash_multimap 的特性于multimap 完全相同。唯一的差别在于它的底层机制是hashtable。也因此,hash_multimap 的元素并不会被自动排序。
hash_multimap 和 hash_map 实现上的唯一差别在于,前者的元素插入操作采用底层机制hashtable的insert_equal(),后者则是采用 insert_unique()。
附录1:SGI STL RB-tree的insert_equal()和insert_unique()插入源码
// 插入新值:节点键值允许重复 // 注意,返回值是一个RB-Tree迭代器,指向新增节点 template <class _Key, class _Value, class _KeyOfValue, class _Compare, class _Alloc> typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::insert_equal(const _Value& __v) {_Link_type __y = _M_header;_Link_type __x = _M_root(); // 从根节点开始while (__x != 0) { // 从根节点开始,往下寻找适当的插入点__y = __x;// _M_key_compare(v, x) =意思是=> (x > v ? true : false)__x = _M_key_compare(_KeyOfValue()(__v), _S_key(__x)) ? _S_left(__x) : _S_right(__x);// 新值遇到比它“大”的节点则往左,否则(遇到比它“小于或等于”的节点)则往右 }return _M_insert(__x, __y, __v);// 以上,x为新值插入点,y为插入点之父节点,v为新值 }// 插入新值:节点键值不允许重复,若重复则插入无效 // 注意,返回值是个pair,第一个元素是个RB-Tree迭代器,指向新增节点, // 第二个元素表示插入成功与否 template <class _Key, class _Value, class _KeyOfValue, class _Compare, class _Alloc> pair<typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator, bool> _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::insert_unique(const _Value& __v) {_Link_type __y = _M_header;_Link_type __x = _M_root(); // 从根节点开始bool __comp = true;while (__x != 0) { // 从根节点开始,往下寻找适当的插入点__y = __x;__comp = _M_key_compare(_KeyOfValue()(__v), _S_key(__x)); // 如果节点值比新值大则往左;否则往右(注意,_KeyOfValue()(__v)为第1参数)__x = __comp ? _S_left(__x) : _S_right(__x);} //离开while循环之后,__y所指即插入点之父节点(此时的它必为叶节点) iterator __j = iterator(__y); // 令迭代器__j指向插入点之父节点__y if (__comp) // 如果离开while循环时comp为真(表示插入节点值“大于”新值,将在左侧插入)if (__j == begin()) // 如果插入点之父节点为最左节点return pair<iterator,bool>(_M_insert(__x, __y, __v), true);// 以上,x为插入点,y为插入点之父节点,v为新值else // 否则(插入点之父节点不为最左节点)--__j; // 调整__j (重新确定插入点,见附录2图) // (1)__comp == false => 插入节点值“小于或等于”新值,右侧插入// (2)__comp == true && 插入点之父节点不为最左节点 => 调整__j(重新确定插入点,见附录2图)if (_M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v)))// 如果新值“大于”插入节点值((注意,_KeyOfValue()(__v)为第2参数))return pair<iterator,bool>(_M_insert(__x, __y, __v), true);// 以上,__x为新值插入点,__y为插入点之父节点,__v为新值// 至此,表示新值一定与树中键值重复,那么就不该插入新值return pair<iterator,bool>(__j, false); }
附录2图:
注:准备插入v=20的值,此时RB-Tree里面已经有一个值为20的节点了。
遍历步骤已经插入流程如下(如图,红色箭头表示遍历路径):
1. 40节点比新值大,往左走;
2. 20节点不比新值大(“小于或等于”),往右走;
3. 30节点比新值大,往左走;同时,达到叶子节点;此时j指向30节点;
4. 30节点不是最左节点,执行“--j”,j新指向20节点;
5. 执行后续判断(也即进入上面代码(1)、(2)情况的语句):
a. 新值是否大于j节点,如果是,插入;
b. 否则(本例子的情况,“小于或等于”),不插入。
注:步骤2(20节点“小于或等于”新值)和步骤5(新值“小于或等于”20节点),由这两个判断可以推断:新值“等于”20节点。进而说明了原RB-Tree中已有相同的值,故而不再插入。
转载于:https://www.cnblogs.com/yyxt/p/4985239.html
STL——关联式容器相关推荐
- STL关联式容器详解
STL关联式容器类别 1. map 定义在 头文件中,使用该容器存储的数据,其各个元素的键必须是唯一的(即不能重复),该容器会根据各元素键的大小,默认进行升序排序(调用 std::less). 2. ...
- STL关联式容器—map的使用
一.map简介: map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值 ...
- C++ STL 关联式容器操作总结
文章目录 set/multiset容器 set/multiset对象的构造 set/multiset的常规操作 set/multiset其他操作 map\multimap容器 map/multimap ...
- STL源码剖析 关联式容器
STL关联式容器以set(集合) 和 map(映射表)两大类,以及对应的衍生体构成,比如mulyiset(多键集合) multimap(多键映射表) ,容器的底层均基于红黑树 RB-Tree也是一个独 ...
- C++ STL : 模拟实现STL中的关联式容器unordered_map/unordered_set
目录 unordered_map/unordered_set unordered_map/unordered_set与map/set的区别 底层哈希桶的改造 仿函数 Key值的获取方法 hash(ke ...
- C++ STL : 模拟实现STL中的关联式容器map和set
目录 关联式容器 键值对 底层红黑树的改造 仿函数 红黑树的迭代器 完整代码 set set的文档介绍 set的实现 map map的文档介绍 map的实现 operator[] 完整代码 multi ...
- C++(STL):29 ---关联式容器map 迭代器
无论是前面学习的序列式容器,还是关联式容器,要想实现遍历操作,就必须要用到该类型容器的迭代器.当然,map 容器也不例外. C++ STL 标准库为 map 容器配备的是双向迭代器(bidirecti ...
- C++(STL):28 ---关联式容器map用法
作为关联式容器的一种,map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对.其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(int.double 等) ...
- STL 容器简介:C++ 容器:顺序性容器、关联式容器和容器适配器
STL标准容器类简介 标准容器类 说明 顺序性容器 vector 从后面快速的插入与删除,直接访问任何元素 deque 从前面或后面快速的插入与删除,直接访问任何元素 list 双链表,从任何地方快速 ...
最新文章
- 程序员的自我修养--链接、装载与库笔记:Windows PE/COFF
- 广告中oCPX到底是如何进行优化的
- oracle pr,PRMSCAN ORACLE碎片扫描合并工具
- 哥德巴赫猜想(洛谷P1304题题解,Java语言描述)
- 打破独立游戏开发者的困局
- python中prompt的意思_PROMPT命令格式是什么意思?
- oracle 上搭建ogg文档,ogg搭建配置实现oracle数据同步到mysql)
- WinAPI: midiOutSetVolume - 设置 MIDI 输出设备的音量
- oracle数据库怎么保存表,oracle从各个表取得数据保存到另一个表
- 武汉ISO27001认证的完整步骤
- ansys选择一个面上所有节点_ansys中如何选择面及其上的节点
- mapminmax()、zscore()数据归一化
- 虚拟偶像养成记:人工智能人格化与IP化打造出完美“爱豆”
- js定义对象的多个属性值
- 串口数据接收、发送与USB转串口驱动下载
- centos7使用iso镜像离线安装依赖工具
- 计算机网络管理员路由与交换深圳积多少分,2020年深圳积分入户,哪些加分的证书总结?...
- 利用pdf2image,将pdf文件转换成图片
- 2021-11-05 奈氏准则,香农定理(考研中的第一波知识点)
- 如何实现实时音视频聊天功能