一、关联式容器

标准的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——关联式容器相关推荐

  1. STL关联式容器详解

    STL关联式容器类别 1. map 定义在 头文件中,使用该容器存储的数据,其各个元素的键必须是唯一的(即不能重复),该容器会根据各元素键的大小,默认进行升序排序(调用 std::less). 2. ...

  2. STL关联式容器—map的使用

    一.map简介: map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值 ...

  3. C++ STL 关联式容器操作总结

    文章目录 set/multiset容器 set/multiset对象的构造 set/multiset的常规操作 set/multiset其他操作 map\multimap容器 map/multimap ...

  4. STL源码剖析 关联式容器

    STL关联式容器以set(集合) 和 map(映射表)两大类,以及对应的衍生体构成,比如mulyiset(多键集合) multimap(多键映射表) ,容器的底层均基于红黑树 RB-Tree也是一个独 ...

  5. C++ STL : 模拟实现STL中的关联式容器unordered_map/unordered_set

    目录 unordered_map/unordered_set unordered_map/unordered_set与map/set的区别 底层哈希桶的改造 仿函数 Key值的获取方法 hash(ke ...

  6. C++ STL : 模拟实现STL中的关联式容器map和set

    目录 关联式容器 键值对 底层红黑树的改造 仿函数 红黑树的迭代器 完整代码 set set的文档介绍 set的实现 map map的文档介绍 map的实现 operator[] 完整代码 multi ...

  7. C++(STL):29 ---关联式容器map 迭代器

    无论是前面学习的序列式容器,还是关联式容器,要想实现遍历操作,就必须要用到该类型容器的迭代器.当然,map 容器也不例外. C++ STL 标准库为 map 容器配备的是双向迭代器(bidirecti ...

  8. C++(STL):28 ---关联式容器map用法

    作为关联式容器的一种,map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对.其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(int.double 等) ...

  9. STL 容器简介:C++ 容器:顺序性容器、关联式容器和容器适配器

    STL标准容器类简介 标准容器类 说明 顺序性容器 vector 从后面快速的插入与删除,直接访问任何元素 deque 从前面或后面快速的插入与删除,直接访问任何元素 list 双链表,从任何地方快速 ...

最新文章

  1. 程序员的自我修养--链接、装载与库笔记:Windows PE/COFF
  2. 广告中oCPX到底是如何进行优化的
  3. oracle pr,PRMSCAN ORACLE碎片扫描合并工具
  4. 哥德巴赫猜想(洛谷P1304题题解,Java语言描述)
  5. 打破独立游戏开发者的困局
  6. python中prompt的意思_PROMPT命令格式是什么意思?
  7. oracle 上搭建ogg文档,ogg搭建配置实现oracle数据同步到mysql)
  8. WinAPI: midiOutSetVolume - 设置 MIDI 输出设备的音量
  9. oracle数据库怎么保存表,oracle从各个表取得数据保存到另一个表
  10. 武汉ISO27001认证的完整步骤
  11. ansys选择一个面上所有节点_ansys中如何选择面及其上的节点
  12. mapminmax()、zscore()数据归一化
  13. 虚拟偶像养成记:人工智能人格化与IP化打造出完美“爱豆”
  14. js定义对象的多个属性值
  15. 串口数据接收、发送与USB转串口驱动下载
  16. centos7使用iso镜像离线安装依赖工具
  17. 计算机网络管理员路由与交换深圳积多少分,2020年深圳积分入户,哪些加分的证书总结?...
  18. 利用pdf2image,将pdf文件转换成图片
  19. 2021-11-05 奈氏准则,香农定理(考研中的第一波知识点)
  20. 如何实现实时音视频聊天功能

热门文章

  1. Python爬虫开发:requests库的使用--ip代理参数的设置
  2. eureka客户端获取服务列表时间间隔配置
  3. Linux wc指令统计文件信息
  4. 请说明一下web.xml文件中可以配置哪些内容?
  5. Rabbitmq消息的Confirm确认机制
  6. win10系统中查找端口
  7. 关于静态局部全局变量
  8. 2013 JavaB2 马虎的算式
  9. windows10 下 vscode + cmake 编译 Qt6 代码
  10. 批处理命令 / echo