算竞常用STL cpp

C++ 标准模板库 (STL, Standard Template Library)

  • 包含一些常用数据结构与算法的模板的 C++ 软件库
  • 包含四个组件
    1. 算法 (Algorithms)->eg:sort(a.begin(), a.end())
    2. 容器 (Containers)(STL实现好的数据结构)->eg:priority_queue<int> pque
    3. 仿函数 (Functors)(使用不多)->eg:greater<int>()
    4. 迭代器 (Iterators)(使用不多)->eg:vector<int>::iterator it = a.begin()

1. STL的好与坏

好 :

  • 简化代码
  • 简化调试

坏 :

  • STL的功能实现较复杂 , 效率低

2. 常用容器

2.1 总览

  • 顺序容器

    • vector
  • 关联容器

    • set
    • map
  • 容器适配器

    • stack
    • queue
    • priority_queue
  • 字符串

    • string (basic_string<char>)
  • 对与元组

    • pair

2.2 向量vector(vector数组)

类似数组 , 长度可变

2.2.1 常用方法

头文件 :

#include <vector>

构造

vector<类型> arr(长度, [初值])

时间复杂度: O ( n ) O(n) O(n)

示例 :

cpp 复制代码
vector<int> arr;         // 构造int数组
vector<int> arr(100);    // 构造初始长100的int数组
vector<int> arr(100, 1); // 构造初始长100的int数组,初值为1

vector<vector<int>> mat(100, vector<int> ());       // 构造初始100行,不指定列数的二维数组
vector<vector<int>> mat(100, vector<int> (666, -1)) // 构造初始100行,初始666列的二维数组,初值为-1
尾接与尾删
  • .push_back(元素) :在 vector 尾接一个元素,数组长度 + 1 +1 +1
  • .pop_back() :删除 vector 尾部的一个元素,数组长度 − 1 -1 −1
长度
  • .size(): 输出数组长度
清空
  • .clear() : 清空所有数组元素
判空
  • .empty() : 空返回 true 反之返回 false
改长
  • .resize(新长度, 默认值) : 修改vector数组的长度
    • 缩短 : 删除多余值
    • 扩大 : 新元素为上述所填的默认值 , 默认值为空则新元素为0
取首与取尾
  • .front()
  • .back()
删除

.erase()

2.2.2 适用场景

  • 部分卡时间复杂度无法用
  • 部分限制内存不能用

2.2.3 注意事项

提前指定长度

.push_back()会浪费时间

size_t 溢出

.size() 返回值类型为 size_t , 32 位编译器类型范围为 [ 0 , 2 32 ) [ 0,2^{32}) [0,232)


2.3 栈stack

先进后出的数据结构

2.3.1 常用方法

头文件

#include <stack>

构造

stack<类型> stk

进栈

.push(元素)

出栈

.pop()

栈顶

.top()

大小

.size()

判空

.empty()

2.3.2 适用场景

部分卡时间不能用

vector也能当栈用

2.3.3 注意事项

不能访问内部元素


2.4 队列 queue

先进先出的数据结构

2.4.1 常用方法

头文件

#include <queue>

构造

queue<类型> que

进队

.push(元素)

出队

.pop()

取队首

.front()

取队尾

.back()

大小

.size()

判空

.empty()

2.4.2 适用场景

不卡常就可以用

2.4.3 注意事项

不可访问内部元素


2.5 优先队列 priority_queue(堆)

  • 底层原理是二叉堆
  • 常数时间的最大元素查找
  • 对数时间的插入与提取

2.5.1 常用方法

头文件

#include <queue>

构造

priority_queue<类型, 容器, 比较器> pque

  • 类型:储存的数据类型
  • 容器:储存数据的底层容器,默认为 vector<类型>
  • 比较器:比较大小使用的比较器,默认为 less<类型>
cpp 复制代码
priority_queue<int> pque1;// 储存int的大顶堆
priority_queue<int, vector<int>, greater<int>> pque2; // 储存int的小顶堆
进堆

.push(元素)

出堆

.pop()

取堆顶

.top()

大小

.size()

判空

.empty()

2.5.3 注意事项

仅堆顶可读
所有元素不可修改
  • 当然堆顶可以出堆+进堆实现

2.6 集合 set

  • 提供对数时间的插入、删除、查找的集合数据结构
  • 底层原理是红黑树
集合三要素 解释 set multiset unordered_set
确定性 一个元素要么在集合中,要么不在
互异性 一个元素仅可以在集合中出现一次 x(任意次)
无序性 集合中的元素是没有顺序的 x(从小到大) x(从小到大)

2.6.1 常用方法

头文件

#include <set>

构造

set<类型, 比较器> st

  • 类型:要储存的数据类型
  • 比较器:比较大小使用的比较器,默认为 less<类型>
cpp 复制代码
set<int> st1;               // 储存int的集合(从小到大)
set<int, greater<int>> st2; // 储存int的集合(从大到小)
遍历

用迭代器进行遍历:

cpp 复制代码
for (set<int>::iterator it = st.begin(); it != st.end(); ++it)
    cout << *it << endl;

范围for(C++ 11):

cpp 复制代码
for (auto &ele : st)
    cout << ele << endl;
插入元素

.insert(元素)

删除元素

.erase(元素)

查找元素

.find(元素)

判断元素是否存在

.count(元素)

大小

.size()

清空

.clear()

判空

.empty()

2.6.2 适用场景

  • 元素去重: [ 1 , 1 , 3 , 2 , 4 , 4 ] → [ 1 , 2 , 3 , 4 ] [1,1,3,2,4,4]\to[1,2,3,4] [1,1,3,2,4,4]→[1,2,3,4]
  • 维护顺序: [ 1 , 5 , 3 , 7 , 9 ] → [ 1 , 3 , 5 , 7 , 9 ] [1,5,3,7,9]\to[1,3,5,7,9] [1,5,3,7,9]→[1,3,5,7,9]
  • 元素是否出现过:元素大小 [ − 10 18 , 10 18 ] [-10^{18},10^{18}] [−1018,1018],元素数量 10 6 10^6 106,vis 数组无法实现,通过 set 可以完成。

2.6.3 注意事项

无下标索引

仅可使用迭代器进行遍历

元素只能读不能改值
不能用迭代器计算下标

2.7 映射 map

  • 提供对数时间的有序键值对结构
  • 底层原理是红黑树

映射 :
1 → 2 2 → 2 3 → 1 4 → 5 ⋮ \begin{matrix} 1&\to&2\\ 2&\to&2\\ 3&\to&1\\ 4&\to&5\\ &\vdots \end{matrix} 1234→→→→⋮2215

eg:

cpp 复制代码
map<string,int>a;
a["abc"] = 3;
a["bcd"] = 2;
性质 解释 map multimap unordered_map
互异性 一个键仅可以在映射中出现一次 x(任意次)
无序性 键是没有顺序的 x(从小到大) x(从小到大)

2.7.1 常用方法

头文件

#include <map>

构造

map<键类型, 值类型, 比较器> mp

  • 键类型:要储存键的数据类型
  • 值类型:要储存值的数据类型
  • 比较器:键比较大小使用的比较器,默认为 less<类型>
cpp 复制代码
map<int, int> mp1;               // int->int 的映射(键从小到大)
map<int, int, greater<int>> st2; // int->int 的映射(键从大到小)
遍历

用迭代器进行遍历:

cpp 复制代码
for (map<int, int>::iterator it = mp.begin(); it != mp.end(); ++it)
    cout << it->first << ' ' << it->second << endl;

范围for(C++ 11):

cpp 复制代码
for (auto &pr : mp)
    cout << pr.first << ' ' << pr.second << endl;
增改查元素

eg: mp[1] = 2;

查元素
  • 返回迭代器

.find(元素)

eg: auto it = mp.find(1)

删除元素

.erase(元素)

判断元素是否存在

.count(元素)

大小

.size()

清空

.clear()

判空

.empty()

2.7.2 适用场景

eg : 输入若干字符串,统计每种字符串的出现次数

2.7.3 注意事项

  1. 如果使用中括号访问 map 时对应的键不存在,那么会新增这个键,并且值为默认值
  2. 不可用迭代器计算下标

2.8 字符串 string

2.8.1 常用方法

头文件

#include <string>

构造

string(长度, 初值)

输入输出
cpp 复制代码
string s;
cin >> s;
cout << s;
修改、查询

eg: s[1] = 'a';

是否相同

eg: if (s1 == s2) ...

字符串连接

eg: string s = s1 + s2;

尾接字符串

eg: s += "abc";

取子串

.substr(起始下标, 子串长度)

eg: string sub = s.substr(2, 10);

查找字符串

.find(字符串, 起始下标)

返回起始下标

eg : int pos = s.find("awa");

cpp 复制代码
if(s.find("123") != string::npos)
{
	cout << "yes" << endl;
}
数值与字符串互转(C++11)
目的 函数
int / long long / float / double / long double string to_string()
string int stoi()
string long long stoll()
string float stof()
string double stod()
string long double stold()

2.9 二元组 pair

储存二元组

2.9.1 常用方法

头文件

#include <utility>

构造

pair<第一个值类型, 第二个值类型> pr

  • 第一个值类型:要储存的第一个值的数据类型
  • 第二个值类型:要储存的第二个值的数据类型
赋值

cpp 复制代码
pair<int, char> pr = make_pair(1, 'a');

新 C++11

cpp 复制代码
pair<int, char> pr = {1, 'a'};
取值

直接取值

  • 取第一个值:.first
  • 取第二个值:.second
判同

直接用 == 运算符


3. 迭代器

3.1 什么是迭代器

eg :

对于一个 vector,可以用下标遍历:

cpp 复制代码
for (int i = 0; i < a.size(); i++)
    cout << a[i] << endl;

也可以用迭代器来遍历:

cpp 复制代码
for (auto it = a.begin(); it != a.end(); ++it)
    cout << *it << endl;
  • a.begin() 是迭代器,指向的是第一个元素
  • a.end() 是迭代器,指向的是最后一个元素再后面一位
  • 迭代器具有自增运算符,自增则迭代器向下一个元素移动
  • 迭代器与指针相似,如果对它使用解引用运算符,即 *it,就能取到对应值了

3.2 用迭代器的原因

很多数据结构并非线性 , 对非线性数据结构来说 , 下标无意义

遍历这种用迭代器就可以

例如,set 的实现是红黑树,没法用下标来访问元素的

但是通过迭代器,就能遍历 set 中的元素:

cpp 复制代码
for (auto it = st.begin(); it != st.end(); ++it)
    cout << *it << endl;

3.3 迭代器的用法

对于 vector 容器:

  • .begin():头迭代器
  • .end():尾迭代器
  • .rbegin():反向头迭代器
  • .rend():反向尾迭代器
  • 迭代器 + 整型:将迭代器向后移动
  • 迭代器 - 整型:将迭代器向前移动
  • 迭代器 ++:将迭代器向后移动 1 位
  • 迭代器 --:将迭代器向前移动 1 位
  • 迭代器 - 迭代器:两个迭代器的距离(整形)
  • prev(it):返回 it 的前一个迭代器
  • next(it):返回 it 的后一个迭代器

3.4 常见问题

.end() .rend() 指向的位置是无意义的值

不同容器的迭代器功能可能不一样

删除操作时需要警惕 -> 无必要,别用迭代器操作容器


4. 常用算法

4.1 总览


4.2 sort()

  • 快速排序
  • 排序可迭代对象

默认排序从小到大

cpp 复制代码
vector<int> arr{1, 9, 1, 9, 8, 1, 0};
sort(arr.begin(), arr.end());
// arr = [0, 1, 1, 1, 8, 9, 9]

如果要从大到小,则需传比较器

cpp 复制代码
vector<int> arr{1, 9, 1, 9, 8, 1, 0};
sort(arr.begin(), arr.end(), greater<int>());
// arr = [9, 9, 8, 1, 1, 1, 0]

如果需要完成特殊比较,则需要手写比较器。

比较器函数返回值是 bool 类型,传参是需要比较的两个元素。记我们定义的该比较操作为 ⋆ \star ⋆:

  • 若 a ⋆ b a\star b a⋆b,则比较器函数应当返回 true
  • 若 a ⋆̸ b a\not\star b a⋆b,则比较器函数应当返回 false

注意 如果 a = b a=b a=b,比较器函数必须返回 false

cpp 复制代码
bool cmp(pair<int, int> a, pair<int, int> b)
{
	//第二位从小到大
    if (a.second != b.second)
        return a.second < b.second;
    //第一位从大到小
    return a.first > b.first;
}

int main()
{
    vector<pair<int, int>> arr{{1, 9}, {2, 9}, {8, 1}, {0, 0}};
	sort(arr.begin(), arr.end(), cmp);
    // arr = [(0, 0), (8, 1), (2, 9), (1, 9)]
}

4.3 swap

交换两个变量的值

eg :

cpp 复制代码
int a = 0, b = 1;
swap(a, b);
// now a = 1, b = 0

int arr[10] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
swap(arr[4], arr[6]);
// now arr = {0, 1, 2, 3, 6, 5, 4, 7, 8, 9}

4.4 lower_bound() / upper_bound()

已升序排序 的元素中,用二分查找检索指定元素,并返回对应元素迭代器位置。找不到则返回尾迭代器。

  • lower_bound(): 寻找 ≥ x \geq x ≥x 的第一个元素的位置
  • upper_bound(): 寻找 > x >x >x 的第一个元素的位置

返回的是迭代器

转成下标索引的方式 : 减去头迭代器即可

cpp 复制代码
vector<int> arr{0, 1, 1, 1, 8, 9, 9};
idx = lower_bound(arr.begin(), arr.end(), 7) - arr.begin(); // 4
idx = lower_bound(arr.begin(), arr.end(), 8) - arr.begin(); // 4
idx = upper_bound(arr.begin(), arr.end(), 7) - arr.begin(); // 4
idx = upper_bound(arr.begin(), arr.end(), 8) - arr.begin(); // 5

4.5 reverse

反转一个可迭代对象的元素顺序

cpp 复制代码
vector<int> arr(10);
iota(arr.begin(), arr.end(), 1);
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
reverse(arr.begin(), arr.end());
// 10, 9, 8, 7, 6, 5, 4, 3, 2, 1

4.6 max() / min()

返回最大值 / 最小值的数值

在 C++11 之后,可以使用列表构造语法传入一个列表,这样就能一次性给多个元素找最大值而不用套娃了:

cpp 复制代码
// Before C++11
int mx = max(max(1, 2), max(3, 4)); // 4
int mn = min(min(1, 2), min(3, 4)); // 1

// After C++11
int mx = max({1, 2, 3, 4}); // 4
int mn = min({1, 2, 3, 4}); // 1

4.7 unique()

消除数组的重复相邻 元素,数组长度不变,但是有效数据缩短

返回的是有效数据位置的结尾迭代器。

例如: [ 1 , 1 , 4 , 5 , 1 , 4 ] → [ 1 , 4 , 5 , 1 , 4 , ? ‾ ] [1,1,4,5,1,4]\to[1,4,5,1,4,\underline?] [1,1,4,5,1,4]→[1,4,5,1,4,?],下划线位置为返回的迭代器指向。

只用unique还无法到去重效果 , 因其只会去除相邻的重复元素, 先排序后用即可

去重后会在末尾产生无效数据 , [ 1 , 1 , 2 , 4 , 4 , 4 , 5 ] → [ 1 , 2 , 4 , 5 , ? ‾ , ? , ? ] [1,1,2,4,4,4,5]\to[1,2,4,5,\underline?,?,?] [1,1,2,4,4,4,5]→[1,2,4,5,?,?,?],为了删掉这些无效数据,需要结合 erase

cpp 复制代码
vector<int> arr{1, 2, 1, 4, 5, 4, 4};
sort(arr.begin(), arr.end());
arr.erase(unique(arr.begin(), arr.end()), arr.end());

4.8 数学函数

所有函数参数均支持 int / long long / float / double / long double

公式 示例
f ( x ) = ∣ x ∣ f(x)=\lvert x\rvert f(x)=∣x∣ 绝对值 abs(-1.0)
f ( x ) = e x f(x)=e^x f(x)=ex 指数 exp(2)
f ( x ) = ln ⁡ x f(x)=\ln x f(x)=lnx 以e为底的对数函数 log(3)
f ( x , y ) = x y f(x,y)=x^y f(x,y)=xy 指数函数 pow(2, 3)
f ( x ) = x f(x)=\sqrt x f(x)=x 平方根 sqrt(2)
f ( x ) = ⌈ x ⌉ f(x)=\lceil x\rceil f(x)=⌈x⌉ 向上取整 ceil(2.1)
f ( x ) = ⌊ x ⌋ f(x)=\lfloor x\rfloor f(x)=⌊x⌋ 向下取整 floor(2.1)
f ( x ) = < x > f(x)=\left<x\right> f(x)=⟨x⟩ 四舍五入 round(2.1)
注意事项

由于浮点误差,有些的数学函数的行为可能与预期不符,导致 WA。如果你的操作数都是整型,那么用下面的写法会更稳妥。

  • ⌊ a b ⌋ \lfloor\frac{a}{b}\rfloor ⌊ba⌋
    • 别用:floor(1.0 * a / b)
    • 要用:a / b
  • ⌈ a b ⌉ \lceil\frac{a}{b}\rceil ⌈ba⌉
    • 别用:ceil(1.0 * a / b)
    • 要用:(a + b - 1) / b ( ⌈ a b ⌉ = ⌊ a + b − 1 b ⌋ \lceil\frac{a}{b}\rceil=\lfloor\frac{a+b-1}{b}\rfloor ⌈ba⌉=⌊ba+b−1⌋)
  • ⌊ a ⌋ \lfloor\sqrt a\rfloor ⌊a ⌋
    • 别用:(int) sqrt(a)
    • 要用:二分查找
  • a b a^b ab
    • 别用:pow(a, b)
    • 要用:快速幂
  • ⌊ log ⁡ _ 2 a ⌋ \lfloor\log\_2 a\rfloor ⌊log_2a⌋
    • 别用:log2(a)
    • 要用:__lg / bit_width(C++20)

4.9 gcd()/ lcm()

(C++17)返回最大公因数 / 最小公倍数

cpp 复制代码
int x = gcd(8, 12); // 4
int y = lcm(8, 12); // 24

如果不是 C++17,但是是 GNU 编译器(g++),那么可以用内置函数 __gcd()

自己写也行(欧几里得算法):

cpp 复制代码
int gcd(int a, int b)
{
    if (!b)
        return a;
    return gcd(b, a % b);
}

int lcm(int a, int b)
{
    return a / gcd(a, b) * b;
}

相关推荐
仟濹2 小时前
【算法打卡day34(2026-03-30 周一)】DFS专项训练(今日算法:DFS & 记忆化搜索 & 回溯)
算法·深度优先
罗湖老棍子2 小时前
【 例 1】区间和(信息学奥赛一本通- P1547)(基础线段树和单点修改区间查询树状数组模版)
数据结构·算法·线段树·树状数组·单点修改 区间查询
tryCbest2 小时前
Python之Flask开发框架(第一篇) — 从安装到第一个应用
开发语言·python·flask
q5431470872 小时前
Java进阶总结——集合
java·开发语言
啥咕啦呛2 小时前
java打卡学习5:java基础学习
java·开发语言·学习
zhangzeyuaaa2 小时前
Python getter/setter 正确用法详解
开发语言·python
南境十里·墨染春水2 小时前
C++ 笔记 深赋值 浅赋值(面向对象)
开发语言·jvm·c++·笔记
旺仔.2912 小时前
常用算法 详解
数据结构·算法
今儿敲了吗2 小时前
算法复盘——差分
数据结构·c++·笔记·学习·算法