STL容器

1、容器概述

1.1、容器分类

1.1.1、顺序容器:提供对元素序列的访问,顺序容器为元素连续分配内存或将元素组织为链表,元素的类型是容器成员value_type

顺序容器 说明
vector<T, A> 空间连续分配的T类型元素序列;默认选择容器
list<T, A> T类型元素双向链表;当需要插入/删除元素但不移动已有元素是选择它。
forward_list<T, A> T类型元素单向链表;很短的或空序列的理想选择
deque<T, A> T类型元素双向队列;向量和链表的混合;对于大多数应用,都比向量和链表其中之一要慢。

模板参数A是一个分配器,容器用它来分配和释放内存, 默认值 std::allocator。

1.1.2、有序关联容器:提供基于关键字的关联查询。

有序关联容器 说明
map<K, V, C, A> 从K到V的有序映射;一个(K, V)对序列
multimap<K, V, C, A> 从K到V的有序映射;允许重复关键字
set<K, C, A> K的有序集合
multiset<K, C, A> K的有序集合;允许重复关键字

C是比较类型,默认值 std::less(K); A是分配器类型,默认值 std::allocator<std::pair<const K, T>>。这些容器通常采用平衡二叉树(通常红黑树)实现。

1.1.3、无序关联容器

有序关联容器 说明
unordered_map<K, V, H, E, A> 从K到V的无序映射;
unordered_multimap<K, V, H, E, A> 从K到V的无序映射;允许重复关键字
unordered_set<K, H, E, A> K的无序集合
unordered_multiset<K, H, E, A> K的无序集合;允许重复关键字

H是关键字类型K的哈希函数类型,默认值 std::hash。E是关键字类型K的相等性测试函数类型, 默认值 std::equal_to, 用来判断哈希值相同的两个对象是否相等。A是分配器类型。这些容器都是采用溢出链表法的哈希表实现。

关联容器都是链接结构(树),节点类型为其成员value_type

1.1.4、容器适配器:提供对底层容器的特殊访问。

容器适配器 说明
priority_queue<T, C, Cmp> T的优先队列;Cmp是优先级函数类型
queue<T, C> T的队列,支持push() 和 pop()操作
stack<T, C> T的栈,支持push() 和 pop()操作

一个priority_queue的默认优先级函数Cmp为std::less。queue的默认容器类型C为std::deque,stack和priority_queue的默认容器类型C为std::vector。

1.2、容器对元素的要求

1.2.1、比较操作

注意 C 风格字符串(即const char*)上的 < 比较的是指针值,因此如果用 C 风格字符串作为关键字,关联容器将不能正常工作,为了让这样的关联容器正常工作,就必须使用基于字典序的比较操作。

struct Cstring_less
{
bool opreator()(const char *p, const char *q) const
{
return strcmp(p, q) < 0;
}
}; map<char*, int, Cstring_less> m; //map使用strcmp()比较const

2、操作

2.1、标准库容器操作复杂性

标准容器操作复杂性
  [] 列表 头部 尾部 迭代器
vector 常量 O(n)+   常量+ 随机
list   常量 常量 常量 双向
forward_list   常量 常量   前向
deque 常量 O(n) 常量 常量 随机
stack       常量  
queue     常量 常量  
priority_queue     O(log(n)) O(log(n))  
map O(log(n)) O(log(n))+     双向
multimap   O(log(n))+     双向
set   O(log(n))+     双向
multimap   O(long(n))+     双向
unordered_map 常量+ 常量+     前向
unordered_multimap   常量+     前向
unordered_set   常量+     前向
unordered_multiset   常量+     前向
string 常量 O(n)+ O(n)+ 常量+ 随机
array 常量       随机
内置数组 常量       随机
valarray 常量       随机
bitset 常量       随机

头部操作:表示在第一个元素之前的插入和删除操作。

尾部操作:表示在最后一个元素之后的插入和删除操作。

列表操作:表示在任意位置的插入和删除操作。

迭代器:“随机”表示随机访问迭代器,“向前”表示前向迭代器,“双向”表示双向迭代器。

时间复杂度

  1. 常量又表示为O(1),表示时间复杂度是个常量,通常代表时间复杂度低。
  2. O(n)表示操作花费非时间与元素数目成正比,元素增加n倍,操作时间也增加n倍。通常代表时间复杂度较高。
  3. O(n*n)表示元素增加n倍,操作时间就增加n^2倍。通常代表时间复杂度很高,n很大时,对资源消耗来说是灾难。
  4. O(logn)表示元素增加n倍,操作时间增加logn倍(以2为底的对数)。通常代表时间复杂度较低。
  5. O(nlogn)表示元素增加n倍,操作时间增加nlogn倍。时间复杂度高于O(n)低于O(n*n)。

时间复杂度后加 “+” 表示时间复杂度可能会更高,如vector增加元素时,可能需要重新分配资源。

值得注意的是,操作效率还取决于计算机的内存和处理器构架的细节,比如通过链接获取下一个元素(list情形)的代价会远高于在一个vector中获取下一个元素的代价(元素是连续存储)。因此对操作效率的评估不能完全简单的依赖于对复杂性的评估,而是要进行实际的测试。

2.2、容器赋值、移动

  1. 赋值操作并不拷贝或移动分配器,目标容器获得一组新的元素,但会保留其旧的分配器,新元素的空间也是用此分配器分配的。

  2. 一个构造函数或是一次元素拷贝可能会抛出异常,来指出它无法完成这个任务。

  3. 初始化器的潜在二义性。

    void use()
    {
    vector<int> vi{1, 3, 5, 7, 9}; //使用5个整数初始化vector
    vector<string> vs(7); //vector初始化为7个空字符串,初始化大小 vector<int> vi2;
    vi2 = {2, 4, 6, 8}; //将4个整数赋予vi2
    vi2.assign(&vi[1], &vi[4]); //将序列3,5,7赋予vi2 vector<string> vs2;
    vs2 = {"hello", "world"}; //赋予vs2两个字符串
    vs2.assign("First", "Second"); //运行时错误
    }

    以上代码中,向vs2赋值时发生的错误时传递了一对指针(而不是一个initalizer_list),而两个指针并非指向相同的数组。记住,对大小初始化器使用(), 而对其它所有初始化器都使用{}

  4. 容器通常都很大,因此几乎总是以引用的方式传递容器实参。但是,由于容器都是资源句柄,因此可以高效的以返回值的方式返回容器(隐含使用移动操作)。类似的不想使用别名时,可以用移动的方式传递容器实参。例如:

    void task(vector<int>&& v);
    //以返回值的方式返回容器,隐式使用移动操作
    vector<int> user(vector<int> &large)
    {
    vector<int> res;
    //...
    task(move(large)); //将数据所有权传递给task(),用移动的方式传递容器实参。
    //...
    return res;
    }

2.3、容器的大小和容量

大小:指容器中的元素数目。

容量:指重新分配更多内存之前容器能够保存的元素数目。

大小和容量
x = c.size() x是c的元素数目
c.empty() c为空吗?
x = c.max_size() x是c的最大可能元素数目
x = c.capacity() x是为c分配的空间大小;只适用于vector和string
c.reserve(n) 为c预留n个元素的空间;只适用于vector和string
c.resize(n) 将c的大小改变为n;将增加的元素初始化为默认元素值;只适用于顺序容器和string
c.resize(n, v) 将c的大小改变为n;将增加的元素初始化为v;只适用于顺序容器和string
c.shrink_to_fit() 令c.capacity()等于c.size();只适用于vector、deque、string
c.clear() 删除c的所有元素

注意,在改变大小或容量时,元素可能会被移动到新的存储位置。这意味着指向元素的迭代器(以及指针和引用)可能会失效(即指向旧元素的位置)。

指向关联容器(如map)元素的迭代器只有当所指元素从容器中删除(erase())时才会失效。与之相反,指向顺序容器(如vector)元素的迭代器当元素重新分配空间(如resize()、reverse()、push_back())或所指元素在容器中移动(如在前一个位置发生了erase()或insert())时也会失效。

2.4、迭代器

容器可以看做按容器迭代器定义的顺序或相反的顺序排列的元素序列。对于一个关联容器,元素的顺序由容器比较标准决定。

迭代器
p = c.begin() p指向c的首元素
p = c.end() p指向c的尾后元素
p = c.cbegin() p指向c的首元素,常量迭代器
p = c.cend() p指向c的尾后元素,常量迭代器
p = c.rbegin() p指向c的反序的首元素
p = c.rend() p指向c的反序的尾后元素
p = c.crbegin() p指向c的反序的首元素,常量迭代器
p = c.crend() p指向c的反序的尾后元素,常量迭代器

更多STL容器细节参考:cplusplus

STL容器概述的更多相关文章

  1. STL——容器概述

    在实际的开发过程中,数据结构本身的重要性完全不逊于算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要. 试想:如同栈一样的一条死胡同里停车,这样的效率会很高吗? 经典的数 ...

  2. C++ STL 容器概述

    在STL编程中,容器是经常用到的一种数据结构,在C++标准库中,容器分为: 序列式容器 关联式容器 二者本质区别在于,序列式容器是通过元素在容器中的位置进行顺序存储和元素访问.关联容器则是通过键[ke ...

  3. STL 容器简介

    一.概述 STL 对定义的通用容器分三类:顺序性容器.关联式容器和容器适配器. 顺序性容器是一种各元素之间有顺序关系的线性表.元素在顺序容器中保存元素置入容器时的逻辑顺序,除非用删除或插入的操作改变这 ...

  4. STL容器

    啦啦啦,今天听啦高年级学长讲的STL容器啦,发现有好多东西还是有必要记载的,毕竟学长是身经百战的,他在参加各种比赛的时候积累的经验可不是一天两天就能学来的,那个可是炒鸡有价值的啊,啊啊啊啊啊 #inc ...

  5. c++ stl容器set成员函数介绍及set集合插入,遍历等用法举例

    c++ stl集合set介绍 c++ stl集合(Set)是一种包含已排序对象的关联容器.set/multiset会根据待定的排序准则,自动将元素排序.两者不同在于前者不允许元素重复,而后者允许. 1 ...

  6. STL容器删除元素的陷阱

    今天看Scott Meyers大师的stl的用法,看到了我前段时间犯的一个错误,发现我写的代码和他提到错误代码几乎一模一样,有关stl容器删除元素的问题,错误的代码如下:std::vector< ...

  7. 【转】c++中Vector等STL容器的自定义排序

    如果要自己定义STL容器的元素类最好满足STL容器对元素的要求    必须要求:     1.Copy构造函数     2.赋值=操作符     3.能够销毁对象的析构函数    另外:     1. ...

  8. GDB打印STL容器内容

    GDB调试不能打印stl容器内容,下载此文件,将之保存为~/.gdbinit就可以使用打印命令了. 打印list用plist命令,打印vector用pvector,依此类推. (gdb) pvecto ...

  9. STL容器迭代器失效分析

    连续内存序列容器(vector, string, deque) 对于连续内存序列STL容器,例如vector,string,deque,删除当前iterator会使得后面所有的iterator都失效, ...

随机推荐

  1. 进程and线程and协程效率对比

    1.进程与进程池的效率对比 多进程:p.start()过程中,只是向操作系统发送一个信号,至于什么时候执行,都是操作系统的事情,操作系统接收到信号时,帮该进程申请一块内存空间+拷贝父进程的地址空间 # ...

  2. WKWebView使用指南|功能丰富的JXBWKWebView

    github地址:JXBWKWebView,如果觉得项目不错可以点个star支持一下,谢谢~ 前言 目前iOS系统已经更新到iOS11,大多数项目向下兼容最多兼容到iOS8,因此,在项目中对WebVi ...

  3. shell脚本之浮点数和整数计算

    整数计算 直接使用放括号计算即可,省去*号需要使用转义符的麻烦 #!/bin/bash num1= num2= var1=$[ $num1 * $num2 ] echo "$var1&quo ...

  4. linux下挂载磁盘

    1.使用fdisk 查看硬盘信息 [root@localhost ~]# fdisk -l Disk /dev/sdb: 107.4 GB, 107374182400 bytes 255 heads, ...

  5. 软件-客户端管理工具-SourceTree:百科

    ylbtech-软件-客户端管理工具-SourceTree:百科 SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端管理工具,同时也是Mn版本控制系统工具 ...

  6. python对象的引用

    1 利用 * 星号生成二维及二维以上的list时,特别要注意有的量引用是相同的.如果后面要给list赋值,最好不要这样生成list. 可以先这样生成,再打印输出后,粘贴到程序中重新赋值. a = [[ ...

  7. Hibernate一级缓冲

    Hibernate的一级缓冲 什么是缓冲 缓冲概念: 数据存在数据库中,数据库本身就是一个文件系统,使用流的方式操作文件,但是文件中有很多的内容,用流的操作得效率就低. 解决办法: 把数据存在内存中, ...

  8. pymysql操作数据库

    pymysql.connect()参数说明:(连接数据库时需要添加的参数)host(str): MySQL服务器地址port(int): MySQL服务器端口号user(str): 用户名passwd ...

  9. Java——ArrayList源码解析

    以下针对JDK 1.8版本中的ArrayList进行分析. 概述     ArrayList基于List接口实现的大小可变的数组.其实现了所有可选的List操作,并且元素允许为任意类型,包括null元 ...

  10. Java String == && equal

    [.net超级群:27921837] Java中equals和==的区别 java中的数据类型,可分为两类:1.基本数据类型,也称原始数据类型.byte,short,char,int,long,flo ...