STL容器的适用情况

1 篇文章 0 订阅

 

STL容器的适用情况

2009-12-16 23:46:55|  分类: C++ |  

一.各种容器的特性

vector

典型的序列容器,C++标准严格要求次容器的实现内存必须是连续的,唯一可以和标准C兼容的stl容器,任意元素的读取、修改具有常数时间复杂度,在序列尾部进行插入、删除是常数时间复杂度,但在序列的头部插入、删除的时间复杂度是O(n),可以 在任何位置插入新元素,有随机访问功能,插入删除操作需要考虑。

deque

序列容器,内存也是连续的,和vector相似,区别在于在序列的头部插入和删除操作也是常数时间复杂度, 可以 在任何位置插入新元素,有随机访问功能。

list  

序列容器,内存是不连续的,任意元素的访问、修改时间复杂度是O(n),插入、删除操作是常数时间复杂度, 可以 在任何位置插入新元素。

set  

关联容器,元素不允许有重复,数据被组织成一棵红黑树,查找的速度非常快,时间复杂度是O(logN)

multiset

关联容器,和set一样,却别是允许有重复的元素,具备时间复杂度O(logN)查找功能。

map  

关联容器,按照{键,值}方式组成集合,按照键组织成一棵红黑树,查找的时间复杂度O(logN),其中键不允许重复。

multimap

和map一样,区别是键可以重复

 

 

二.容器的一种分类

连续内存的容器:这种类型容器包含vector、deque。特点是在一块连续的内存块上存放数据,所以有数据插入和删除的时候,如果不是在序列的 或者两端那么花费的代价是非常大的,因为需要保证连续内存,同时给新元素腾出空间或者填充删除元素的空间,如果存储的是复杂结构的话就要花费大量的时间进行拷贝操作(可以存储复杂结构的指针来弥补这个缺陷,这个讨论在另个总结中进行)。

基于节点的容器:这类容器是剩余的几个list、set、multiset、map、multimap.这类容器中的数据是分别存储在不同的内存块中,可能连续也可能不连续(一般不认为是连续的),这样的容器在插入删除元素的时候修改的只是节点的指针,这样的消耗是非常小的。

 

三. 使用中需要考虑的一些因素

在使用的过程中,需要考虑的问题有元素顺序、标准的一致性、迭代器能力、内存布局和C的兼容性、查找速度这些,考虑了这些问题你选择的容器应该会非常适合你当前的情景。

1.       需要大量添加新元素

vector在大量添加元素的时候问题最大,因为他的一种最常见的内存分配实现方法是当前的容量(capacity)不足就申请一块当前容量2倍的新内存空间,然后将所有的老元素全部拷贝到新内存中,添加大量元素的时候的花费的惊人的大。如果由于其他因素必须使用vector,并且还需要大量添加新元素,那么可以使用成员函数reserve来事先分配内存,这样可以减少很多不必要的消耗。

list对这种情况的适应能力就非常好,都是常数时间的插入消耗。deque前面说过了,他是vector和list的折衷形式,内存不够了就申请一块新的内存,但并不拷贝老的元素。

2.       查找速度

这个因素主要取决于算法,而算法最终是作用在容器中元素上的,所以这里的查找速度指的是容器能够达到的最好查找效率。

对于序列容器需要分两种情况,区分依据是元素是否排序,1)对于已经排序的序列容器,使用binary_search、lower_bound、upper_bound、equal_range可以获得对数时间复杂度的查找速度(O(logN));2)而未排序的序列容器二分查找肯定是用不了,能达到的最好的时间复杂度是线性的(O(n))。

对于关联容器,存储的时候存储的是一棵红黑树(一种更为严格的平衡二叉树,文档最后有介绍),总是能达到对数时间复杂度(O(logN))的效率,因为关联容器是按照键值排好序的。

3.       是否是连续内存

连续内存的容器有个明显的缺点,就是有新元素插入或老元素删除的时候,为了给新元素腾出位置或者填充老元素的空缺,同一块内存中的其他数据需要进行整体的移位,这种移位的拷贝代价有时是非常巨大的。标准容器中的vector、deque是连续内存的,其中vector是完全连续内存,而deque是vector和list的折衷实现,是多个内存块组成的,每个块中存放的元素连续内存,而内存块又像链表一样连接起来。

所以需要考虑在操作的过程中是否有在任意位置插入元素的需求,有这种需求的话尽量避免使用连续内存的vector、deque

 

4.       元素的排序

序列容器中的元素不会自动排序,程序员插入什么顺序内存中就是什么顺序,而关联容器不是这样的,他会以自己的键值按照某种等价关系(equivalence)进行排序。所以默认情况下序列容器中的元素是无序的,而关联容器中的元素是有序的。

所以容器在遍历元素的时候序列容器输出的顺序和插入的顺序式一致的,关联容器就不一定了。下面给出两个例子:

STL容器的适用情况 - 韩少文 - → 螃 蟹 ←   的 地 盘

输出结果如下:

 STL容器的适用情况 - 韩少文 - → 螃 蟹 ←   的 地 盘

通过例子看到序列容器vector遍历的顺序和插入的顺序是一样的,而关联容器set把插入的元素按照某种顺序重新组织了,所以选择容器的时候如果很在意插入顺序的话就选择序列容器。

5.       内存是否和C兼容

适合的容器只有一个vector,意思就是如果需要把容器中的数据放到C类型的数组中那么不需要做多余复杂的操作,如果有vector<int> v,只需要直接使用&v[0]就可以得到v中第一个元素的指针,因为vector和C数组的内存布局是一样的,这个要求同时也是标准C++委员会制定的标准。所以能保证有这样特性的容器只有vector,那么vector以外的其他STL容器中的数据如果需要变换成C数组形式,或者C数组放到其他类型容器中,可以把vector作为一个桥梁,下面给个例子:

//假设函数void read(const int* pInt, unsigned int num);

//pInt指针位置开始读取numint型数据

std::set<int> mSet;

... //省略给mSet插入元素的操作

std::vector<int> mVector(mSet.begin(), mSet.end());

if (!mVector.empty())

read(&mVector[0], mVector.size());

 

四.各种容器的优缺点:

用哪种容器的选择看起来非常繁琐,头脑中如果有个每个容器大概的模型,在选择的时候会更为轻松点。

1.       Vector的数据模型就是数组。

优点:内存和C完全兼容、高效随机访问、节省空间

缺点:内部插入删除元素代价巨大、动态大小查过自身容量需要申请大量内存做大量拷贝。

2.       List 的数据结构模型是链表

优点:任意位置插入删除元素常量时间复杂度、两个容器融合是常量时间复杂度

缺点:不支持随机访问、比vector占用更多的存储空间

3.       Deque的数据模型是数组和链表的折衷:

优点:高效随机访问、内部插入删除元素效率方便、两端push pop

缺点:内存占用比较高

4.       Map、set、multimap、multiset的数据结构模型是二叉树(红黑树)

优点:元素会按照键值排序、查找是对数时间复杂度、通过键值查元素、map提供了下标访问

 

五.综合一下该用什么?

 

首先说说vector、list、deque

1) 如果需要随机访问,用vector

2) 如果存储元素的数目已知,用vector

3) 需要任意位置随机插入删除,用list

4) 只有需要更多在容器的首部尾部插入删除元素,用deque

5) 元素是复杂结构用list,也可以用vector存储指针(需要额外的精力去维护内存),看需求

6) 如果操作是基于键值,用set map

7) 如果需要经常的搜索,用map  set

8) map set 的区别是map中的元素都是pair<key, value>,同时map提供下标访问[] ,也是个陷阱,这个Once在日志里讲过

 STL容器的适用情况 - 韩少文 - → 螃 蟹 ←   的 地 盘

如果实在不想细看上面说的繁琐的内容,就照下面这个流程走一遍,就会找到最终的归宿

这个是照着一个老外的个人主页上总结的图画的

原始链接:http://www.linuxsoftware.co.nz/containerchoice.png

附录:

标准关联容器实现的红黑树,这个大哥(姐)讲的挺好的

http://hi.baidu.com/20065562/blog/item/93b2d17fd6f391320dd7da44.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ STL(Standard Template Library)是C++标准库中的一个重要组成部分,提供了一组数据结构算法的模板类,可以大大简化C++程序的开发过程。STL包含了多个容器类,每个容器类都有其特定的特性和用途。 STL中的容器类主要分为序列容器和关联容器两大类。序列容器包括vectorlist、deque和array,它们按照元素在容器中的位置进行存储和访问。关联容器包括set、multiset、map、multimap和unordered系列容器,它们按照键值进行存储和访问。 序列容器具有以下特性: 1. 动态大小:序列容器可以根据需要动态调整大小,可以在任意位置插入和删除元素。 2. 快速随机访问:序列容器中的元素可以通过索引快速访问,时间复杂度为O(1)。 3. 按顺序存储:序列容器中的元素按照插入的顺序存储,并保持元素的相对位置不变。 4. 支持迭代器:序列容器提供了迭代器,可以通过迭代器遍历容器中的元素。 关联容器具有以下特性: 1. 自动排序:关联容器中的元素按照键值自动排序,并且可以根据自定义的比较函数进行排序。 2. 快速查找:关联容器支持快速的查找操作,时间复杂度为O(log n)。 3. 不允许重复键值:set和map容器中的键值是唯一的,而multiset和multimap容器允许重复的键值。 4. 无序容器:unordered系列容器是C++11引入的,它们使用哈希函数来存储和访问元素,查找操作的平均时间复杂度为O(1)。 总而言之,C++ STL提供了丰富的容器类,每个容器类都有其独特的特性和适用场景,可以根据具体需求选择合适的容器存储和操作数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++——STL容器](https://blog.csdn.net/JAN6055/article/details/122758690)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++的STL容器类详解](https://blog.csdn.net/Jinyizhi2233/article/details/131640448)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值