[转]STL 容器一些底层机制
1、vector 容器
vector 的数据安排以及操作方式,与 array 非常相似。两者的唯一区别在于空间的运用的灵活性。array 是静态空间,一旦配置了就不能改变,vector 是动态数组。在堆上分配空间。vector 是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素(有保留内存,如果减少大小后内存也不会释放。如果新值>当前大小时才会再分配内存,这大大影响了 vector 的效率,)。因此,vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必因为害怕空间不足而一开始要求一个大块的 array。
vector
动态增加大小,并不是在原空间之后持续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大的空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对
vector 的任何操作,一旦引起空间重新配置,同时指向原vector
的所有迭代器就都失效了。
对最后元素操作最快(在后面添加删除最快),此时一般不需要移动内存。对中间和开始处进行添加删除元素操作需要移动内存。如果你的元素是结构或是类,那么移动的同时还会进行构造和析构操作,所以性能不高(最好将结构或类的指针放入 vector 中,而不是结构或类本身,这样可以避免移动时的构造与析构)。访问方面,对任何元素的访问都是
O(1),也就是常数时间的。
总结:
vector 常用来保存需要经常进行随机访问的内容,并且不需要经常对中间元素进行添加删除操作。
2、list 容器
相对于
vector 的连续空间,list 就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间,元素也是在堆中。因此,list
对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素移除,永远是常数时间。STL 中的list 底层是一个双向链表,而且是一个环状双向链表。这个特点使得它的随即存取变的非常没有效率,因此它没有提供
[] 操作符的重载。
总结:
如果你喜欢经常添加删除大对象的话,那么请使用 list;
要保存的对象不大,构造与析构操作不复杂,那么可以使用 vector 代替。
list<指针> 完全是性能最低的做法,这种情况下还是使用 vector<指针> 好,因为指针没有构造与析构,也不占用很大内存
3、deque 容器
deque
是一种双向开口的连续线性空间,元素也是在堆中。所谓双向开口,意思是可以在队尾两端分别做元素的插入和删除操作。deque 和 vector
的最大差异,一在于 deque 允许于常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓容量观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接在一起。换句话说,像
vector 那样“因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在 deque 是不会发生的。它的保存形式如下:
[堆1] --> [堆2] -->[堆3] --> ...
deque
是由一段一段的定量连续空间构成。一旦有必要在 deque 的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个 deque
的头端或尾端。deque
的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置,复制,释放”的轮回,代价则是复杂的迭代器架构。因为有分段连续线性空间,就必须有中央控制器,而为了维持整体连续的假象,数据结构的设计及迭代器前进后退等操作都颇为繁琐。
deque 采用一块所谓的 map 作为主控。这里的 map 是一小块连续空间,其中每个元素都是指针,指向另一段连续线性空间,称为缓冲区。缓冲区才是 deque 的存储空间主体。( 底层数据结构为一个中央控制器和多个缓冲区)SGI STL 允许我们指定缓冲区大小,默认值
0 表示将使用 512 bytes 缓冲区。
支持[]操作符,也就是支持随即存取,可以在前面快速地添加删除元素,或是在后面快速地添加删除元素,然后还可以有比较高的随机访问速度和vector
的效率相差无几。deque
支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与
list 的效率也差不多。
在标准库中 vector 和 deque
提供几乎相同的接口,在结构上区别主要在于在组织内存上不一样,deque 是按页或块来分配存储器的,每页包含固定数目的元素;相反 vector
分配一段连续的内存,vector 只是在序列的尾段插入元素时才有效率,而 deque 的分页组织方式即使在容器的前端也可以提供常数时间的
insert 和 erase 操作,而且在体积增长方面也比 vector 更具有效率。
总结:
vector 是可以快速地在最后添加删除元素,并可以快速地访问任意元素;
list 是可以快速地在所有地方添加删除元素,但是只能快速地访问最开始与最后的元素;deque
在开始和最后添加元素都一样快,并提供了随机访问方法,像vector一样使用 []
访问任意元素,但是随机访问速度比不上vector快,因为它要内部处理堆跳转。deque 也有保留空间。另外,由于 deque
不要求连续空间,所以可以保存的元素比 vector
更大,这点也要注意一下。还有就是在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。
因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用 vector;
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用 list;3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
4、stack
stack 是一种先进后出(First In Last Out , FILO)的数据结构。它只有一个出口,stack 允许新增元素,移除元素,取得最顶端元素。但除了最顶端外,没有任何其它方法可以存取stack的其它元素,stack不允许遍历行为。
以某种容器( 一般用 list 或 deque 实现,封闭头部即可,不用
vector 的原因应该是容量大小有限制,扩容耗时)作为底部结构,将其接口改变,使之符合“先进后出”的特性,形成一个
stack,是很容易做到的。deque 是双向开口的数据结构,若以 deque
为底部结构并封闭其头端开口,便轻而易举地形成了一个stack。因此,SGI
STL 便以 deque 作为缺省情况下的 stack 底部结构,由于 stack
系以底部容器完成其所有工作,而具有这种“修改某物接口,形成另一种风貌”之性质者,称为 adapter(配接器),因此,STL stack
往往不被归类为 container(容器),而被归类为 container adapter。
5、 queue
queue
是一种先进先出(First In First Out,FIFO)的数据结构。它有两个出口,queue
允许新增元素,移除元素,从最底端加入元素,取得最顶端元素。但除了最底端可以加入,最顶端可以取出外,没有任何其它方法可以存取 queue
的其它元素。
vector 的原因应该是容量大小有限制,扩容耗时)作为底部结构,将其接口改变,使之符合“先进先出”的特性,形成一个
queue,是很容易做到的。deque 是双向开口的数据结构,若以 deque
为底部结构并封闭其底部的出口和前端的入口,便轻而易举地形成了一个 queue。因此,SGI STL 便以 deque
作为缺省情况下的 queue 底部结构,由于 queue 系以底部容器完成其所有工作,而具有这种“修改某物接口,形成另一种风貌”之性质者,称为
adapter(配接器),因此,STL queue 往往不被归类为container(容器),而被归类为 container adapter。
6、heap
heap
并不归属于 STL 容器组件,它是个幕后英雄,扮演 priority queue(优先队列)的助手。priority queue
允许用户以任何次序将任何元素推入容器中,但取出时一定按从优先权最高的元素开始取。按照元素的排列方式,heap 可分为 max-heap 和
min-heap 两种,前者每个节点的键值(key)都大于或等于其子节点键值,后者的每个节点键值(key)都小于或等于其子节点键值。因此,
max-heap 的最大值在根节点,并总是位于底层array或vector的起头处;min-heap
的最小值在根节点,亦总是位于底层array或vector起头处。STL 供应的是 max-heap,用 C++ 实现。
堆排序 C++ 语言实现:点击这里。
7、priority_queue
priority_queue
是一个拥有权值观念的 queue,它允许加入新元素,移除旧元素,审视元素值等功能。由于这是一个
queue,所以只允许在底端加入元素,并从顶端取出元素,除此之外别无其它存取元素的途径。priority_queue
带有权值观念,其内的元素并非依照被推入的次序排列,而是自动依照元素的权值排列(通常权值以实值表示)。权值最高者,排在最前面。缺省情况下
priority_queue 系利用一个 max-heap 完成,后者是一个以vector 表现的 complete binary
tree.max-heap 可以满足 priority_queue 所需要的“依权值高低自动递减排序”的特性。
priority_queue 完全以底部容器(一般为vector为底层容器)作为根据,再加上
heap 处理规则,所以其实现非常简单。缺省情况下是以 vector 为底部容器。queue
以底部容器完成其所有工作。具有这种“修改某物接口,形成另一种风貌“”之性质者,称为 adapter(配接器),因此,STL
priority_queue 往往不被归类为 container(容器),而被归类为 container adapter。
8、set 和 multiset 容器
set 的特性是,所有元素都会根据元素的键值自动被排序。set 的元素不像 map 那样可以同时拥有实值(value)和键值(key),set 元素的键值就是实值,实值就是键值,set不允许两个元素有相同的值。set 底层是通过红黑树(RB-tree)来实现的,由于红黑树是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的
STL 的 set 即以 RB-Tree 为底层机制。又由于 set 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 set 操作行为,都只有转调用 RB-tree 的操作行为而已。
multiset的特性以及用法和 set 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制是 RB-tree 的 insert_equal() 而非 insert_unique()。
9、map 和 multimap 容器
map的特性是,所有元素都会根据元素的键值自动被排序。map 的所有元素都是 pair,同时拥有实值(value)和键值(key)。pair 的第一元素被视为键值,第二元素被视为实值。map不允许两个元素拥有相同的键值。由于
RB-tree 是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL map 即以 RB-tree 为底层机制。又由于 map 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 操作行为,都只是转调 RB-tree 的操作行为。
multimap 的特性以及用法与 map 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique。
10、hash_set
和 hash_multiset 容器
hash_set 底层数据结构为 hash 表,无序,不重复。
hash_multiset 底层数据结构为 hash 表,无序,不重复。
11、hash_map 和 hash_multimap 容器
hash_map 底层数据结构为 hash 表,无序,不重复。
hash_multimap 底层数据结构为 hash 表,无序,不重复。
[转]STL 容器一些底层机制的更多相关文章
- C++ STL容器底层机制
1.vector容器 vector的数据安排以及操作方式,与array非常相似.两者的唯一区别在于空间的运用的灵活性.array是静态空间,一旦配置了就不能改变.vector是动态空间,随着元素的加入 ...
- STL容器底层数据结构的实现
C++ STL 的实现: 1.vector 底层数据结构为数组 ,支持快速随机访问 2.list 底层数据结构为双向链表,支持快速增删 3.deque ...
- STL容器共性机制和使用场景
一.STL容器共性机制 STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是说当我们给容器中插入元素的时候,容器内部实施了拷贝动作,将我们要插入的元素再另行拷贝一份放 ...
- STL - 容器共性机制研究
C++模板是容器的概念. 理论提高:所有容器提供的都是值(value)语意,而非引用(reference)语意.容器执行插入元素的操作时,内部实施拷贝动作.所以STL容器内存储的元素必须能够被拷贝(必 ...
- STL 容器简介
一.概述 STL 对定义的通用容器分三类:顺序性容器.关联式容器和容器适配器. 顺序性容器是一种各元素之间有顺序关系的线性表.元素在顺序容器中保存元素置入容器时的逻辑顺序,除非用删除或插入的操作改变这 ...
- STL 容器的概念
STL 容器的概念 在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要. 经典的数据结构数量有限,但是我们 ...
- 标准非STL容器 : bitset
1. 概念 什么是"标准非STL容器"?标准非STL容器是指"可以认为它们是容器,但是他们并不满足STL容器的所有要求".前文提到的容器适配器stack.que ...
- STL容器
啦啦啦,今天听啦高年级学长讲的STL容器啦,发现有好多东西还是有必要记载的,毕竟学长是身经百战的,他在参加各种比赛的时候积累的经验可不是一天两天就能学来的,那个可是炒鸡有价值的啊,啊啊啊啊啊 #inc ...
- STL容器的遍历删除
STL容器的遍历删除 今天在对截包程序的HashTable中加入计时机制时,碰到这个问题.对hash_map中的每个项加入时间后,用查询函数遍历hash_map,以删除掉那些在表存留时间比某个阈值长的 ...
随机推荐
- Idea(三)常用插件以及快捷键总结
idea常用插件以及快捷键 现在开发中和日常自己开发都统一换成了idea进行开发了.现在针对自己常用到的插件和快捷键进行总结记录下. 插件 Alibaba Java Coding Guidelines ...
- Java多线程(四) —— 线程并发库之Atomic
一.从原子操作开始 从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包,而Queue,很多情况下使用到了Atomic操作,因此首先从这里开始). 很多情况下 ...
- UVAlive4287_Proving Equivalences
题意是告诉你有n个命题,m条递推关系,表示某个命题可以推出另外一个命题. 现在问你至少在增加多少个递推关系可以保证所有命题两两互推. 命题为点,关系为有向边,题目转化成为至少增加多少条有向边使得整个图 ...
- 数据挖掘领域经典分类算法 —— C4.5算法(附python实现代码)
目录 理论介绍 什么是分类 分类的步骤 什么是决策树 决策树归纳 信息增益 相关理论基础 计算公式 ID3 C4.5 python实现 参考资料 理论介绍 什么是分类 分类属于机器学习中监督学习的一种 ...
- BZOJ2159 Crash 的文明世界 【第二类斯特林数 + 树形dp】
题目链接 BZOJ2159 题解 显然不能直接做点分之类的,观察式子中存在式子\(n^k\) 可以考虑到 \[n^k = \sum\limits_{i = 0} \begin{Bmatrix} k \ ...
- 洛谷 P1270 “访问”美术馆 解题报告
P1270 "访问"美术馆 题目描述 经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动.艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向 ...
- hadoop进阶----hadoop经验(一)-----生产环境hadoop部署在超大内存服务器的虚拟机集群上vs几个内存较小的物理机
生产环境 hadoop部署在超大内存服务器的虚拟机集群上 好 还是 几个内存较小的物理机上好? 虚拟机集群优点 虚拟化会带来一些其他方面的功能. 资源隔离.有些集群是专用的,比如给你三台设备只跑一个 ...
- 前端学习 -- Css
Css:Cascading Style Sheets CSS叫做层叠样式表,用来设置页面中元素的样式.背景颜色.字体颜色.字体大小... 编写位置: 1,内联样式: 将样式编写到标签的style属性中 ...
- 关于表单中Readonly和Disabled
Readonly和Disabled是用在表单中的两个属性,它们都能够做到使用户不能够更改表单域中的内容.但是它们之间有着微小的差别,总结如下: Readonly只针对input(text / pass ...
- 一个最简单的使用Entity Framework 查询SQL 数据库的例子
1.ADO.NET 3.5 Entity Framework是随着.net framework 3.5一起发布的,确认开发环境版本是大于等于3.5版本 2.确认已经安装了ADO.NET 3.5 Ent ...