C++ STL 容器简单讲解
STL 简单讲解
网上有很多很好的资料可以参考
而直接看标准是最准确清晰的
vectorstackqueue/priority_queuedeque
arraymap/multimapset/multisetunordered_mapunordered_set- 关于指针和迭代器
- !!!
pbds - ……
本文默认认为读者会基本的 STL 应用。
一切 STL 从 \(0\) 开始左闭右开!
顺序容器
vector 是最常用的,动态数组。
vector<int> v(n, 0)v.reserve(n)预分配空间,加速push_backv.push_back(x)v.clear()只是移动指针,但是不会改变占用的内存!v.shrink_to_fit()缩减内存到恰好有v.size()个。
其常用的复杂度如下:
- 随机访问:\(O(1)\)
- 在末尾删除或者加入元素:均摊 \(O(1)\)
- 插入或删除一个元素:与到
vector末尾的距离成线性 \(O(n)\)。
还有一些常用的成员函数:
v.at(i)访问指定位置的元素,进行越界检查(聊胜于无)v.front(), v.back()访问开头/结尾的元素,在空容器上调用是未定义行为v.empty() / v.size() / v.resize()\(O(n)\) 删除/加入一个元素的写法,删除需要保证一定存在!
v.erase(lower_bound(begin(v), end(v), x));
v.insert(lower_bound(begin(v), end(v), x), x);
离散化的操作? \(O(n \log n + n)\)
sort(begin(v), end(v));
v.erase(unique(begin(v), end(v)), end(v));
begin(v)和v.begin()等价,end(v)同理
deque 是神秘的双端队列。
与 std::vector 相反,deque 的元素不是相接存储的:典型实现用单独分配的固定尺寸数组的序列,外加额外的登记,这表示下标访问必须进行二次指针解引用,与之相比 vector 的下标访问只进行一次。
摘自 cppreference
也就是唯一与 vector 的区别在于可以 \(O(1)\) 的 push_front() / pop_front()。
其他的东西和 vector 一模一样(只是常数比较大)。

stack 和 queue 以及 priority_queue 称为容器适配器。
该类模板表现为底层容器的包装器—,即只提供特定函数集合。
默认情况下,stack 和 queue 都是使用 deque 作为底层,所以常数很大。
一般来说,stack 应当用 vector 代替,queue 手写即可(除非不知道到底会有多少元素入队)。
priority_queue 底层默认是 vector,其常数仍然很大……但是还是有替代品(一般不需要)
在 algorithm 库中有 make_heap / push_heap / pop_heap / sort_heap 操作,但我声称没有必要。
还有三个存在感极差的顺序容器:
array<T, N>就是 STL 中的静态数组,一般用在套 vector 的情况:vector< array<int, 2> > f(n);list / forward_list可以用在邻接表中,但是不如用vector快。
在这些顺序容器上可以有一些神秘的 STL 操作:
#define all(x) begin(x), end(x)
auto it = find(all(v), x)\(O(n)\) 的在数组中寻找首个 \(x\) 的位置,无返回end(v)。auto it = lower/upper_bound(all(v), x)\(O(\log n)\) 的在有序的数组中寻找 \(x\) 的位置。int cnt = count(all(v), x)\(O(n)\) 的返回其中 \(x\) 的个数。reverse(all(v))\(O(n)\) 翻转。merge(all(v1), all(v2), back_inserter(res))\(O(n + m)\) 的归并两个有序数组。T res = max/min(all(v))\(O(n)\) 的返回最大/最小元素。pair<T, T> res = minmax(all(v))\(O(n)\) 的返回一个pair。iota(all(v), x)循环赋值 \(v[i] = x + i\)。fill(all(v), x) / fill_n(begin(v), siz, x)赋值。- ……
关联容器
分为两大类:
- 有序
map和set和multi...:- 红黑树
- 都是 \(O(\log n)\) 单次操作
- 无序
unordered_map和unoredered_set和unordered_multi...:- 哈希表
- 平均 \(O(1)\),最坏 \(O(n)\)
有序容器上的操作
s.lower_bound(x)才是 \(O(\log n)\) 的s.find(x)如果没找到返回的是end(s)- 在
multiset上执行count操作是 \(O(\log n + s)\) 的,\(s\) 是元素的个数。
无序上的操作
s.reserve(n)预留 \(n\) 个元素的空间,减少多次插入的时间。
迭代器
有四种迭代器,但是一般只会用到一种:正向迭代器
begin(x), end(x) 返回的就是指向容器开头,末尾的迭代器。
对于顺序容器,操作返回的迭代器为 随机访问迭代器,而关联容器(和 list)返回的则是 双向迭代器。
对于随机访问迭代器,我们可以 it +/- x,而双向迭代器只能 ++it / --it。
对于空迭代器(例如 end(v))的操作是未定义行为,可能 RE,也可能无事发生。
vector<int> v(10, -1);
iota(begin(v), begin(v) + 5, 0);
vector<int>::iterator it = find(begin(v), end(v), 4);
// int* 也可以看作是随机访问迭代器
int a[100];
fill(a, a + 50, 7);
// for (int i = 0; i < 50; ++i) a[i] = 7;
int *it = lower_bound(a, a + 50, 8); // it == a + 50
失效的迭代器
写珂朵莉树或多或少都知道:
auto itr = split(r + 1), itl = split(l); // 顺序不能颠倒,否则可能RE
这是因为 split(r + 1) 操作可能影响到 l 所在的节点,导致迭代器失效。
不会修改容器的方法一定不会使迭代器或引用失效。修改容器内容的方法可能会使迭代器和/或引用失效。
对于 vector,后面的操作不会影响前面迭代器,如果容量变化也会失效。
vector<int> v(10);
auto it = begin(v) + 5;
v.insert(begin(v) + 7); // it 不失效
v.insert(begin(v)); // it 失效
v.resize(5) // it 失效
对于 deque,可以认为只要修改了内容迭代器就失效了。
对于 map, set, multi... 认为一直有效,除非本身被 erase 或容器被 clear。
对于 unordered_... 也认为一直情况有效,除非插入导致重哈希。
STL 的字符串
读入一行:
string s;
cin >> std::ws;
getline(cin, s);
关于 std::string 的那些事……
可以 clear / insert / pop/push_back / erase / resize / reserve / ...,类似于 vector<char>。
只是多了 s.substr(pos, len) 返回子串 \([pos, pos + len)\) 或者 \([pos, size())\)。
在 \(pos \gt size()\) 时会报错(RE),复杂度与 \(len\) 成线性。
如果不需要对原字符串进行修改,但是需要获取子串,推荐 string_view。
string s = "ni hao guai er zi";
string_view v(s);
string_view sub = v.substr(3, 3); // sub == "hao", O(1);
sub.remove_prefix(1); // sub == "ao", O(1);
sub.remove_suffix(1); // sub == "a", O(1);
然而,我声称可以定义广义字符串:
basic_string<int> v;
for (int i = 0; i < 5; ++i) v += i;
// v = {0, 1, 2, 3, 4}
/* 以下是 C++17 及以上的行为 */
basic_string_view<int> vi(v);
vi.remove_prefix(2);
for (int x : vi) cout << x << ' ';
C++ STL 容器简单讲解的更多相关文章
- 史上最全的各种C++ STL容器全解析
史上最全的C++ STL 容器大礼包 为什么\(C++\)比\(C\)更受人欢迎呢?除了\(C++\) 的编译令人感到更舒适,\(C++\)的标准模板库(\(STL\))也占了很重要的原因.当你还在用 ...
- STL容器删除元素的陷阱
今天看Scott Meyers大师的stl的用法,看到了我前段时间犯的一个错误,发现我写的代码和他提到错误代码几乎一模一样,有关stl容器删除元素的问题,错误的代码如下:std::vector< ...
- 标准C++中的STL容器类简单介绍
SGI -- Silicon Graphics[Computer System] Inc.硅图[计算机系统]公司. STL -- Standard Template Library 标准模板库. ...
- 不要在公共接口中传递STL容器
最近的一个项目,是开发一个framework,提供给公司内部不同的产品线使用. 之间遇到的一个问题,就是STL容器的使用, 而结论是不要在公共接口中传递STL容器: 这里说的STL容器,但主要则是指容 ...
- STL容器的内存分配
这篇文章参考的是侯捷的<STL源码剖析>,所以主要介绍的是SGI STL实现版本,这个版本也是g++自带的版本,另外有J.Plauger实现版本对应的是cl自带的版本,他们都是基于HP实现 ...
- 转:STL容器里存放对象还是指针
一.问题的引出: 容器可以存放对象,可以存放指针,这里要谈的是两者的使用问题.就是什么时候存放对象更好,什么时候存放指针更好? 二.问题的分析过程: 1. 首先说下stl容器的工作方式 对于内建类 ...
- STL容器底层数据结构的实现
C++ STL 的实现: 1.vector 底层数据结构为数组 ,支持快速随机访问 2.list 底层数据结构为双向链表,支持快速增删 3.deque ...
- STL容器之一vector
STL中最简单也是最有用的容器之一是vector<T>类模板,称为向量容器,是序列类型容器中的一种. 1.vector<T> 对象的基本用法(1)声明:vector<ty ...
- STL容器与拷贝构造函数
所有容器提供的都是“value语意”而非“reference语意”.容器内进行元素的安插操作时,内部实施的是拷贝操作,置于容器内.因此STL容器 的每一个元素都必须能够拷贝.---<<C+ ...
- STL 容器的概念
STL 容器的概念 在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要. 经典的数据结构数量有限,但是我们 ...
随机推荐
- shell编程-发送消息
需求:利用 Linux 自带的 mesg 和 write 工具,编写一个向用户快速发送消息的脚本,输入用户名作为第一个参数,消息内容为第二个参数.脚本需要检测用户是否登录,是否打开消息功能,以及当前发 ...
- 云原生时代Go最受欢迎Web开源框架Gin原理与实战
@ 目录 概述 定义 特点 概览导图 使用 快速入门 HTTP 方法使用 参数获取 参数绑定 自定义日志输出 自定义中间件 路由组 HTML渲染 设置和获取Cookie XML.YAML.ProtoB ...
- 天翼云SD-WAN解决方案直播
2023年6月16日14点,天翼云SD-WAN解决方案直播火热来袭啦!参与直播即可领取优惠好礼,实惠多多! 点击链接注册参与:https://ctyun.d1meeting.cn/0616/ 直播时间 ...
- Framework 中使用 Toolkit.Mvvm 的生成器功能
.NET Standard是.NET APIs的正式规范,可在多个.NET实现中使用..NET Standard的动机是为了在.NET生态系统中建立更大的统一性..NET 5及更高版本采用了不同的方法 ...
- 关于Abp Vnext 权限授权的问题
一.问题 最近收到一位朋友的求助,说他项目上的权限授权出现了问题,现象是在基础服务授权角色:RC 权限:X.Default,在基础服务使用RC角色的用户登录能访问到权限X.Default资源,而在X服 ...
- 完全兼容DynamoDB协议!GaussDB(for Cassandra)为NoSQL注入新活力
摘要:DynamoDB是一款托管式的NoSQL数据库服务,支持多种数据模型,广泛应用于电商.社交媒体.游戏.IoT等场景. 本文分享自华为云社区<完全兼容DynamoDB协议!GaussDB(f ...
- 特性介绍 | MySQL测试框架 MTR 系列教程(四):语法篇
作者:卢文双 资深数据库内核研发 序言: 以前对 MySQL 测试框架 MTR 的使用,主要集中于 SQL 正确性验证.近期由于工作需要,深入了解了 MTR 的方方面面,发现 MTR 的能力不仅限于此 ...
- IoTOS-v1.2.1接入J-IM(t-io)后台通知App
IoTOS v1.2.1 一.登录页增加可修改轮播 登录页增加可修改数据轮播: 首页轮播图由背景图片.标题.介绍.按钮一.按钮二(可配置跳转地址打开方式)组合而成 二.登录页增加常用运营商平台& ...
- 【Java 新的选择】,Solon v2.3.8 发布
Solon 是什么开源项目? 一个,Java 新的生态型应用开发框架.它从零开始构建,有自己的标准规范与开放生态(历时五年,已有全球第二级别的生态规模).与其他框架相比,它解决了两个重要的痛点:启动慢 ...
- 图像处理_Retinex图像增强
单尺度SSR (Single Scale Retinex) 图像 S ( x , y ) S(x,y) S(x,y)分解为两个不同的图像:反射图像 R ( x , y ) R(x,y) R(x,y), ...