STL¶
方法速览¶
| 任务 | 方法 | 口诀 |
|---|---|---|
| 最值 | *min_element(a, a+n) / *max_element(...) |
取星号才到值 |
| 去重 | 先 sort 再 unique,返回尾后迭代器 + erase |
先排再去 |
| 二分三件套 | lower_bound(≥)、upper_bound(>)、binary_search(在不在) |
lower 带等号 |
| 小根堆 | priority_queue<int, vector<int>, greater<int>> |
默认大根,要小根写 greater |
| scanf 读 double | 必须 %lf;printf 输出用 %f(%lf 也行) |
入严格出宽松 |
STL 函数速查¶
| 函数 | 用法 | 说明 |
|---|---|---|
swap(a, b) |
交换两变量 | 内置类型、容器都可 |
min(a, b) / max(a, b) |
取小 / 大 | 同类型才可比 |
min_element / max_element |
*min_element(a, a+n) |
返回迭代器,加 * 取值 |
unique |
unique(v.begin(), v.end()) |
只去相邻重复,先 sort |
lower_bound |
lower_bound(a, a+n, x) - a |
第一个 ≥ x 的下标 |
upper_bound |
同上 | 第一个 > x 的下标 |
binary_search |
返回 bool | 只判存在,不给位置 |
sqrt(x) / pow(x, y) |
<cmath> |
返回 double |
log2(x) / log10(x) |
自然/常用对数 | 换底 \(\log_a b = \log b / \log a\) |
isupper / islower / isdigit |
判字符类别 | 返回 bool |
toupper / tolower |
大小写转换 | char(toupper(c)) |
strcmp |
strcmp(s1, s2) |
0 相等、<0 / >0 比大小 |
memset / memcpy |
按字节填 / 拷贝 | memset(a, 0, sizeof a);只能填 0 / -1 |
fopen / fclose |
文件读写 | 比赛用 freopen 重定向 |
reverse |
reverse(a, a+n) |
原地翻转 |
count / find |
计数 / 找位置 | 线性扫描 |
memset 陷阱:它按字节填充——memset(a, 1, sizeof a) 得到的是 0x01010101(16843009),不是 1;常用值只有 0 和 −1(全 1 字节)。
scanf/printf 格式详解¶
| 类型 | scanf | printf | 备注 |
|---|---|---|---|
int |
%d |
%d |
|
long long |
%lld |
%lld |
Windows 旧编译器曾用 %I64d,现代无碍 |
float |
%f |
%f |
|
double |
%lf |
%f(%lf 亦可) |
输入必须 %lf |
char |
%c |
%c |
不跳过空白(空格回车照读) |
| 字符串(char 数组) | %s |
%s |
scanf 遇空白停 |
格式化控制:
| 写法 | 效果 | 例 |
|---|---|---|
%5d |
场宽 5,右对齐 | ␣␣␣42 |
%-5d |
左对齐 | 42␣␣␣ |
%02d |
补零到 2 位 | 07 |
%.2f |
保留 2 位小数(四舍五入) | 3.14 |
%05.1f |
补零 + 1 位小数 | 007.5 |
%c 不跳空白是高频坑:scanf("%d", &n); scanf("%c", &c); 会把换行读进 c——中间加 getchar() 或用 %c(前置空格跳过空白)。
容器全表¶
| 容器 | 头文件 | 底层 | 核心操作 | 特点 |
|---|---|---|---|---|
string |
<string> |
动态字符数组 | + size() substr [] |
可增长、无 \0 概念 |
vector |
<vector> |
动态数组 | push_back size() [] pop_back |
尾部 O(1) |
array |
<array> |
定长数组 | [] size() |
编译期定长,带 STL 接口 |
pair |
<utility> |
两个值 | first second |
按字典序比较 |
stack |
<stack> |
适配器 | push top pop |
LIFO |
queue |
<queue> |
适配器 | push front pop |
FIFO |
priority_queue |
<queue> |
堆 | push top pop |
默认大根堆 |
deque |
<deque> |
双端块 | push_front/back |
两端 O(1) |
list |
<list> |
双链表 | push_front/back splice |
不支持下标 |
set |
<set> |
红黑树 | insert erase count |
有序去重,\(O(\log n)\) |
multiset |
<set> |
红黑树 | 同上 | 有序可重复 |
map |
<map> |
红黑树 | mp[key] count erase |
有序键值对 |
unordered_set/map |
<unordered_...> |
哈希表 | 同上 | 无序,均摊 O(1) |
priority_queue 大小根:
set 的 count 只返回 0/1(存在性);map 的 [] 在键不存在时会自动插入默认值——查询不想插入要用 count/find。
迭代器¶
*it取值、it->member访问成员(pair/结构体);end()是尾后位置,遍历条件写it != v.end();- 迭代器失效:
erase后原迭代器失效,用返回值接住:it = v.erase(it);;边遍历边删是事故高发区。
set/map 常用技巧¶
| 需求 | 写法 |
|---|---|
| 最小键 | *s.begin() |
| 最大键 | *s.rbegin() |
| 第一个 ≥ x | *s.lower_bound(x) |
| 第一个 > x | *s.upper_bound(x) |
| 元素存在? | s.count(x)(set 版返回 0/1) |
| 删除值为 x 的全部(multiset) | s.erase(x);只删一个用 s.erase(s.find(x)) |
例:set<int> s = {3, 1, 4, 1, 5}(自动去重排序为 {1,3,4,5}):*s.begin() = 1、*s.rbegin() = 5、*s.lower_bound(3) = 3、s.count(1) = 1。
练习题目¶
练习 1 · min_element 返回值¶
题目
int a[] = {5, 2, 9}; 取最小值的正确写法( )
- A.
min_element(a, a+3) - B.
min(a, a+3) - C.
*min_element(a, a+3) - D.
a.min()
答案与解析
答案:C
min_element 返回迭代器(指针),要加 * 解引用;A 打印出来是地址。
练习 2 · min/max 的参数¶
题目
max(3, 5.5) 的结果( )
- A. \(5.5\)
- B. \(5\)
- C. 编译错误(类型不同)
- D. 不确定
答案与解析
答案:C
max 要求两参数同类型;写 max(3.0, 5.5) 或 max<double>(3, 5.5)。
练习 3 · unique 的前提¶
题目
unique(v.begin(), v.end()) 直接去重的前提是( )
- A. 无需任何前提
- B. 元素都是偶数
- C. v 是 set
- D. 序列已排序(它只去相邻重复)
答案与解析
答案:D
unique 只把相邻重复挪到后面——标准套路是 sort → unique → erase 三连。
练习 4 · 去重三连¶
题目
vector 去重的完整套路是( )
- A.
sort→unique→erase(unique(...), end()) - B. 只
unique - C. 只
sort - D.
reverse→unique
答案与解析
答案:A
unique 把重复挪到尾部并返回新尾后位置,erase 才真正删掉;只 sort 不去重、只 unique 不排序都会翻车。
练习 5 · lower_bound 语义¶
题目
lower_bound(a, a+n, x) - a 得到( )
- A. 第一个 > x 的下标
- B. 第一个 ≥ x 的下标
- C. x 的下标
- D. 最后一个 ≤ x 的下标
答案与解析
答案:B
lower 带等号;upper 才是严格大于。
练习 6 · upper_bound¶
题目
有序数组 \(\{1, 3, 3, 5\}\),upper_bound(a, a+4, 3) - a =( )
- A. \(1\)
- B. \(2\)
- C. \(3\)
- D. \(4\)
答案与解析
答案:C
第一个 > 3 的位置是下标 3(元素 5);顺带:lower_bound(3) = 1、两者之差 3−1 = 2 恰是 3 的个数。
练习 7 · binary_search¶
题目
binary_search(a, a+n, x) 返回( )
- A. 下标
- B. bool(存在与否)
- C. 值
- D. 迭代器
答案与解析
答案:B
它只回答“在不在”;要位置用 lower/upper_bound。
练习 8 · pow 的返回类型¶
题目
cout << pow(2, 10); 可能输出 1024 也可能输出 1023.999... 截断问题——稳妥写法( )
- A.
(int)pow(2, 10)之前先round:int(pow(2,10) + 0.5) - B. 直接
(int)pow(2,10)一定安全 - C. pow 只能算浮点不能用
- D. 用
2 ^ 10
答案与解析
答案:A
pow 返回 double 有微小误差,截断可能差 1——四舍五入再转 int;D 的 ^ 是异或不是乘方(=8)。
练习 9 · 大小写转换¶
题目
把字符 c 转成大写( )
- A.
(char)c.upper() - B.
c + 26 - C.
c * 2 - D.
c - 'a' + 'A'或toupper(c)
答案与解析
答案:D
ASCII 差 32:'A' - 'a' = -32;toupper 是标准库版本(非字母原样返回)。
练习 10 · strcmp¶
题目
strcmp("abc", "abd") 的返回值( )
- A. \(0\)
- B. 负数
- C. 正数
- D. \(1\)
答案与解析
答案:B
逐字符比较到 'c' < 'd' 返回负数——只保证符号,不保证返回 −1。
练习 11 · memset 陷阱¶
题目
memset(a, 1, sizeof(int) * 4) 后 a[0] 的值是( )
- A. \(1\)
- B. \(16843009\)(0x01010101)
- C. \(0\)
- D. 未定义
答案与解析
答案:B
memset 按字节填:每字节 0x01,int 四字节拼成 0x01010101——只有 0 和 −1 整数安全。
练习 12 · double 的输入格式¶
题目
double x; 用 scanf 读入的格式符( )
- A.
%f - B.
%lf - C.
%d - D.
%c
答案与解析
答案:B
输入必须 %lf(float 才是 %f);printf 输出 double 用 %f 即可。
练习 13 · %c 的空白¶
题目
输入3\nA,c 得到( )
- A.
'A' - B.
'\n' - C.
'3' - D. 空格
答案与解析
答案:B
%c 不跳过空白,换行被吃掉——改用 scanf(" %c", &c)(% 前加空格)先跳空白。
练习 14 · 补零输出¶
题目
printf("%02d:%02d", 7, 5); 输出( )
- A.
7:5 - B.
07:05 - C.
7: 5 - D.
7.5
答案与解析
答案:B
0 前缀补零、宽度 2——时间格式输出标准写法。
练习 15 · 两位小数¶
题目
printf("%.2f", 3.14159); 输出( )
- A.
3.1 - B.
3.142 - C.
3.15 - D.
3.14
答案与解析
答案:D
.2 保留两位小数(第三位四舍五入,此处 1 舍去)。
练习 16 · 场宽¶
题目
printf("[%5d]", 42); 输出( )
- A.
[00042] - B.
[42␣␣␣] - C.
[42] - D.
[␣␣␣42]
答案与解析
答案:D
宽度 5 默认右对齐左补空格;%-5d 左对齐、%05d 补零。
练习 17 · vector 的操作¶
题目
vector<int> v = {1, 2}; 执行 v.push_back(3); v.pop_back(); 后 v 是( )
- A.
{1, 2, 3} - B.
{1} - C.
{1, 2} - D.
{2, 3}
答案与解析
答案:C
push 尾进、pop 尾出——一进一出回到原状。
练习 18 · 默认 priority_queue¶
题目
priority_queue<int> q 依次 push 3、1、4 后 q.top() 是( )
- A. \(1\)
- B. \(3\)
- C. \(4\)
- D. \(3.5\)
答案与解析
答案:C
默认大根堆,top 是最大值——“默认弹大,要小写 greater”。
练习 19 · 小根堆写法¶
题目
定义小根堆的正确写法( )
- A.
priority_queue<int> q; - B.
priority_queue<int, vector<int>, greater<int>> q; - C.
priority_queue<int, less<int>> q; - D.
queue<int> q;
答案与解析
答案:B
三参数缺一不可(中间的 vector 是底层容器);C 是大根(less 同默认)。
练习 20 · set 的有序性¶
题目
set<int> s; 依次 insert 3、1、2 后遍历输出( )
- A. 不确定
- B.
3 1 2(插入序) - C.
3 2 1 - D.
1 2 3(升序)
答案与解析
答案:D
set 底层红黑树,遍历即有序;再插 1 也不重复。
练习 21 · set 判存在¶
题目
判断值 x 是否在 set s 中,可写( )
- A.
s.count(x)或s.find(x) != s.end() - B.
s[x] - C.
s.at(x) - D.
s.top()
答案与解析
答案:A
set 没有 [](那是 map 的);count 返回 0/1。
练习 22 · map 的 [] 副作用¶
题目
map<string,int> mp; 执行 cout << mp["apple"]; 后( )
- A. 输出 0 且 apple 被插入
- B. 只输出 0
- C. 编译错误
- D. 输出 -1
答案与解析
答案:A
[] 对不存在的键自动插入默认值(int → 0)——纯查询用 count/find 防止误插。
练习 23 · unordered 与有序¶
题目
unordered_set 与 set 的核心区别( )
- A. unordered 不能去重
- B. unordered 哈希实现、无序、均摊 O(1);set 红黑树、有序、O(log n)
- C. set 更快
- D. unordered 只能存整数
答案与解析
答案:B
要“有序遍历 / 前驱后继”用 set;只要“查在不在”用 unordered 更快。
练习 24 · 取 set 最值¶
题目
set s 中取最小键与最大键( )
- A. 做不到
- B.
s.min()与s.max() - C.
s.front()与s.back() - D.
*s.begin()与*s.rbegin()
答案与解析
答案:D
begin 指最小、rbegin 反向指向最大;C 是 vector/deque 的接口。
练习 25 · 迭代器基础¶
题目
v.end() 指向( )
- A. 最后一个元素
- B. 第一个元素
- C. 最后一个元素的下一位置(尾后)
- D. 空位置 0
答案与解析
答案:C
左闭右开区间 [begin, end)——遍历条件写 it != v.end() 的原因。
练习 26 · 边遍历边删¶
题目
遍历 vector 删除所有偶数,正确写法( )
- A.
for (auto it = v.begin(); it != v.end(); ) if (*it % 2 == 0) it = v.erase(it); else ++it; - B. erase 后仍
++it - C. 直接
v.erase(it)后继续用 it - D. 用 for-each 循环里 erase
答案与解析
答案:A
erase 返回下一个有效迭代器,删了不 ++、没删才 ++;B/C/D 都会踩迭代器失效。
易错小结¶
min_element/max_element返回迭代器要*;min/max要求同类型;- unique 只去相邻重复:sort → unique → erase 三连;
- lower_bound ≥、upper_bound >、binary_search 只判在不在;
- memset 只能填 0 和 −1(按字节);pow 有浮点误差,转 int 先 +0.5;
- scanf 读 double 必须 %lf;
%c不跳空白(前加空格%c);%02d补零、%.2f两位小数、%5d右对齐场宽; - priority_queue 默认大根,小根要
greater<int>三参数; - map 的
[]会自动插入默认值,纯查询用 count/find; - set/map 有序 O(log n)、unordered 无序 O(1);最小
*begin()、最大*rbegin(); end()是尾后;erase 用返回值接住迭代器,边遍历边删别 ++。