ACM入门之【STL】

46 阅读9分钟

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()); 先按照第一关键字从小到大排,再按照第二关键字从小到大排。

image.png

常见应用场景举例

应用场景示例说明
图的边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<类型, 大小> 名称;

image.png

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> dqO(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] = valueO(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 + s2O(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)统计出现次数