STL(Standard Template Library)标准模板库,STL是C++标准库的重要组成部分,它提供了一系列可复用的通用数据结构和算法组件,极大提高了C++程序的开发效率。在ACM竞赛中,熟练掌握STL可以让你更专注于算法逻辑而非底层实现。
pair
什么是
pair
?
在ACM竞赛中,
pair
是一种简单的数据结构,用于将两个值组合成一个单元。它起源于C++标准模板库(STL)中的std::pair
模板类。
常见的使用方式如下:
pair<int, string> p = {1, "apple"};
pair<int,int>a(3,5);
pair<double,double>b;
b=make_pair(12.3,144);
pair<int,pair<int,int>>p;
vector< pair<int,int> >ve;
sort(ve.begin(),ve.end()); 先按照第一关键字从小到大排,再按照第二关键字从小到大排。
常见应用场景举例
应用场景 | 示例说明 |
---|---|
图的边 | pair<int, int> 表示边 (u, v) |
权值边 | pair<int, pair<int, int>> 表示边权、起点、终点 |
坐标 | pair<int, int> 表示二维平面坐标 |
区间 | pair<int, int> 表示区间 [l, r] |
排序依据 | vector<pair<int, string>> 排序学生成绩与姓名 |
优先队列 | Dijkstra 的堆:priority_queue<pair<int, int>> |
array(定长数组)
std::array
是 C++11 引入的一个固定大小的容器,封装了原始数组,提供了更多功能(如支持begin()
,end()
,size()
等函数),使用起来比普通数组更安全、易用。它比vector要快好多。
基本语法:
#include <array>
std::array<类型, 大小> 名称;
stack(栈)
stack
是一种常用的数据结构,它在 C++ 中由标准模板库 STL(Standard Template Library)提供,位于头文件<stack>
中。它是一种后进先出(LIFO,Last In First Out)的结构,就像一个只能从顶部放入和取出物品的箱子。它基于其他容器(默认是deque)实现,提供了一组特定的操作接口。
#include <stack>
using namespace std;
stack<int> s; // 创建一个存储int类型的栈
主要操作:
push(x)
:将元素x压入栈顶pop()
:弹出栈顶元素(不返回)top()
:访问栈顶元素(不弹出)empty()
:判断栈是否为空size()
:返回栈中元素数量
queue(队列)
queue是一种先进先出(FIFO, First In First Out)的数据结构容器适配器,它基于其他容器(默认是deque)实现,提供了一组特定的操作接口。
queue通常这样声明:
#include <queue>
using namespace std;
queue<int> q; // 创建一个存储int类型的队列
主要操作:
push(x)
:将元素x加入队尾pop()
:移除队头元素(不返回)front()
:访问队头元素(不移除)back()
:访问队尾元素empty()
:判断队列是否为空size()
:返回队列中元素数量
priority_queue(优先队列)
STL中的priority_queue(优先队列)是一种特殊的队列,其中的元素按照优先级顺序而非插入顺序出队。它实际上是一个堆(heap) 数据结构,默认实现为大顶堆(最大元素优先)。
通常这样声明:
#include <queue>
using namespace std;
priority_queue<int> pq; // 默认大顶堆
priority_queue<int, vector<int>, greater<int>> min_pq; // 小顶堆
主要操作:
push(x)
:插入元素O(log n)pop()
:删除堆顶元素O(log n)top()
:访问堆顶元素O(1)empty()
:判断是否为空size()
:返回元素数量
deque(双端队列)
deque
(双端队列,全称 Double-Ended Queue)是C++标准模板库(STL)中的一种动态数组容器,支持在头部和尾部高效地插入和删除元素(时间复杂度均为 O(1)O(1))。
它类似于vector
,但比vector
更灵活,因为vector
只能在尾部高效操作,而deque
两端都能快速操作。
基本操作
操作 | 函数 | 时间复杂度 | 说明 |
---|---|---|---|
尾部插入元素 | push_back(x) | O(1) | 在末尾添加元素 x |
头部插入元素 | push_front(x) | O(1) | 在开头添加元素 x |
尾部删除元素 | pop_back() | O(1) | 移除末尾元素 |
头部删除元素 | pop_front() | O(1) | 移除开头元素 |
访问头部元素 | front() | O(1) | 返回第一个元素(不删除) |
访问尾部元素 | back() | O(1) | 返回最后一个元素(不删除) |
随机访问元素 | operator[] 或 at(i) | O(1) | 访问第 i 个元素(从0开始) |
容量操作
操作 | 函数 | 时间复杂度 | 说明 |
---|---|---|---|
判断是否为空 | empty() | O(1) | 返回 bool ,空为 true |
返回元素数量 | size() | O(1) | 返回当前元素个数 |
清空所有元素 | clear() | O(n) | 移除所有元素 |
其他操作
操作 | 函数 | 时间复杂度 | 说明 |
---|---|---|---|
插入元素 | insert(pos, x) | O(n) | 在指定位置 pos 插入 x |
删除元素 | erase(pos) | O(n) | 删除指定位置 pos 的元素 |
构造函数 | deque<T> dq | O(1) | 默认构造空 deque |
拷贝构造函数 | deque<T> dq2(dq1) | O(n) | 拷贝另一个 deque |
vector
vector
是一种动态数组容器,它能够根据需要自动调整大小。与普通数组相比,vector的主要优势在于它的灵活性和提供的丰富操作方法。
基本操作及时间复杂度
操作 | 代码示例 | 时间复杂度 | 说明 |
---|---|---|---|
访问元素 | v[i] 或 v.at(i) | O(1) | 随机访问,与数组相同 |
尾部插入 | v.push_back(x) | 平均 O(1),最坏 O(n) | 可能需要重新分配内存 |
尾部删除 | v.pop_back() | O(1) | 仅删除不释放内存 |
头部插入 | v.insert(v.begin(), x) | O(n) | 需要移动所有元素 |
头部删除 | v.erase(v.begin()) | O(n) | 需要移动所有元素 |
中间插入 | v.insert(pos, x) | O(n) | 取决于插入位置 |
中间删除 | v.erase(pos) | O(n) | 取决于删除位置 |
获取大小 | v.size() | O(1) | 当前元素数量 |
获取容量 | v.capacity() | O(1) | 当前分配的内存大小 |
改变大小 | v.resize(n) | O(n) | 可能需要初始化新元素 |
预留空间 | v.reserve(n) | O(n) | 预先分配内存避免多次扩容 |
清空容器 | v.clear() | O(n) | 析构所有元素,但可能不释放内存 |
判断空 | v.empty() | O(1) | 检查是否为空 |
首元素 | v.front() | O(1) | 等价于 v[0] |
末元素 | v.back() | O(1) | 等价于 v[v.size()-1] |
交换内容 | v1.swap(v2) | O(1) | 仅交换指针,不复制元素 |
set(集合)
set
是一种关联式容器,它存储唯一的元素,并且这些元素会自动排序。可以把它想象成一个不允许重复元素的自动排序集合。set通常基于红黑树(一种自平衡的二叉搜索树)实现,这保证了:1. 元素唯一性:不会存储重复值 2. 自动排序:元素总是按升序排列(默认)3. 高效操作:查找、插入、删除都是O(log n)时间复杂度。
set的常用操作及时间复杂度
操作 | 代码示例 | 时间复杂度 | 说明 |
---|---|---|---|
插入 | s.insert(x) | O(log n) | 插入元素 |
删除 | s.erase(x) | O(log n) | 删除元素 |
查找 | s.find(x) | O(log n) | 查找元素 |
计数 | s.count(x) | O(log n) | 检查存在(0/1) |
大小 | s.size() | O(1) | 元素数量 |
空检查 | s.empty() | O(1) | 是否为空 |
清空 | s.clear() | O(n) | 删除所有元素 |
首元素 | *s.begin() | O(1) | 最小元素 |
末元素 | *s.rbegin() | O(1) | 最大元素 |
下界 | s.lower_bound(x) | O(log n) | ≥x的第一个元素 |
上界 | s.upper_bound(x) | O(log n) | >x的第一个元素 |
map
map
是一种关联容器,它存储的是键值对(key-value pairs) ,并且会根据键(key)自动排序。可以把它想象成一个自动整理且查找迅速的"字典"或"电话簿"。
map的常用操作及时间复杂度
操作 | 代码示例 | 时间复杂度 | 说明 |
---|---|---|---|
插入 | m[key] = value 或 m.insert({key, value}) | O(log n) | 插入键值对 |
访问 | m[key] | O(log n) | 不存在时会创建 |
查找 | m.find(key) | O(log n) | 返回迭代器 |
计数 | m.count(key) | O(log n) | 检查键是否存在(0/1) |
删除 | m.erase(key) | O(log n) | 删除键值对 |
大小 | m.size() | O(1) | 键值对数量 |
空检查 | m.empty() | O(1) | 是否为空 |
清空 | m.clear() | O(n) | 删除所有元素 |
首元素 | m.begin() | O(1) | 最小键的迭代器 |
末元素 | m.rbegin() | O(1) | 最大键的迭代器 |
下界 | m.lower_bound(key) | O(log n) | ≥key的第一个位置 |
上界 | m.upper_bound(key) | O(log n) | >key的第一个位置 |
unordered_map
unordered_map
是一种基于哈希表实现的关联容器,它存储键值对(key-value pairs) ,但不自动排序。可以把它想象成一个超级高效的"无序字典",查找速度通常比map更快。
unordered_map的常用操作及时间复杂度
操作 | 代码示例 | 平均时间复杂度 | 最坏时间复杂度 | 说明 |
---|---|---|---|---|
插入 | um[key] = value | O(1) | O(n) | 插入键值对 |
访问 | um[key] | O(1) | O(n) | 不存在时会创建 |
查找 | um.find(key) | O(1) | O(n) | 返回迭代器 |
计数 | um.count(key) | O(1) | O(n) | 检查键是否存在(0/1) |
删除 | um.erase(key) | O(1) | O(n) | 删除键值对 |
大小 | um.size() | O(1) | O(1) | 键值对数量 |
空检查 | um.empty() | O(1) | O(1) | 是否为空 |
清空 | um.clear() | O(n) | O(n) | 删除所有元素 |
string
string
是专门用于处理字符串的容器类,它比传统的C风格字符数组(char[])更安全、功能更强大。可以把它想象成一个"智能字符数组",自带各种便捷的字符串操作功能。
string的常用操作及时间复杂度
操作 | 代码示例 | 时间复杂度 | 说明 |
---|---|---|---|
访问字符 | s[i] 或 s.at(i) | O(1) | 随机访问 |
拼接 | s1 + s2 | O(n+m) | 连接两个字符串 |
长度 | s.length() | O(1) | 返回字符数 |
查找 | s.find(sub) | 平均O(n) | 找子串位置 |
子串 | s.substr(pos, len) | O(len) | 提取子串 |
插入 | s.insert(pos, str) | O(n) | 在指定位置插入 |
删除 | s.erase(pos, len) | O(n) | 删除子串 |
替换 | s.replace(pos, len, str) | O(n) | 替换子串 |
比较 | s1.compare(s2) | O(n) | 比较字符串 |
algorithm
<algorithm>
头文件提供了一系列高效预置算法,它们不是容器,而是操作容器的工具集。可以把它想象成一个"算法工具箱",里面装满了各种现成的数据处理工具。
algorithm常用函数及时间复杂度
算法 | 函数示例 | 时间复杂度 | 说明 |
---|---|---|---|
排序 | sort(beg, end) | O(n log n) | 快速排序 |
稳定排序 | stable_sort(beg, end) | O(n log n) | 保持相等元素顺序 |
部分排序 | partial_sort(beg, mid, end) | O(n log k) | 排序前k个元素 |
第n元素 | nth_element(beg, nth, end) | O(n) | 使第n位处于正确位置 |
二分查找 | binary_search(beg, end, val) | O(log n) | 检查存在性 |
下界 | lower_bound(beg, end, val) | O(log n) | 找第一个≥val的位置 |
上界 | upper_bound(beg, end, val) | O(log n) | 找第一个>val的位置 |
反转 | reverse(beg, end) | O(n) | 反转序列 |
去重 | unique(beg, end) | O(n) | 相邻重复去重 |
全排列 | next_permutation(beg, end) | O(n) | 生成下一个排列 |
填充 | fill(beg, end, val) | O(n) | 填充值 |
最值 | max_element(beg, end) | O(n) | 找最大值位置 |
计数 | count(beg, end, val) | O(n) | 统计出现次数 |