【算法基础】STL复习总结
本文总结了C++ STL中常用容器的核心特性和基本操作,主要包括: 1)顺序容器(vector/string/deque)支持随机访问和动态扩容;2)容器适配器(queue/stack/priority_queue)提供特定数据结构的封装;3)有序关联容器(set/map)基于红黑树实现自动排序;4)无序关联容器基于哈希表实现高效查找;5)bitset用于位级操作。重点解析了vector的动态数组
STL总览
1. STL 一句话总览
- 顺序容器:
vector / deque / string(按插入顺序存) - 容器适配器:
queue / stack / priority_queue(用底层容器封装出特定行为) - 关联容器(有序):
set / map / multiset / multimap(红黑树,自动有序,O(log n)) - 无序关联容器:
unordered_*(哈希表,均摊O(1)) - 位集:
bitset(按位存储/运算,非常省空间)
vector(动态数组)
size():当前元素个数(O(1))empty():是否为空(等价size()==0)clear():清空所有元素(size变 0;容量capacity一般不一定变小)front():第一个元素的引用(非空才能用)back():最后一个元素的引用(非空才能用)push_back(x):尾部插入元素(均摊O(1),扩容时可能O(n))pop_back():删除尾部元素(非空才能用)begin()/end():返回迭代器区间[begin, end)(end 指向“尾后”)operator[] (i):下标访问第 i 个元素(O(1),不做越界检查)比较运算(<,==,...):按字典序比较(先比第 0 个,不同就决定;否则继续)
常见坑:
front/back/pop_back对空容器是未定义行为;[]越界也不会报错。
pair<int,int>(二元组)
first:第一个成员second:第二个成员比较运算:字典序比较(先比first,再比second)
用途:坐标、边 (u,v)、(value, id)、map/set 里当键都很常见。
string(字符串)
size()/length():字符串长度(等价,O(1))empty():是否为空clear():清空字符串,变成""substr(pos, len):从pos开始取子串,长度len(len省略则到末尾)- 若
pos超过长度会抛异常(std::out_of_range)
- 若
c_str():返回底层 C 风格字符串指针const char*,以\0结尾- 指针在字符串被修改后可能失效(如
push_back/+=/clear等)
- 指针在字符串被修改后可能失效(如
queue(队列 FIFO)
size():元素个数empty():是否为空push(x):入队(从队尾插入)front():查看队头元素(非空才能用)back():查看队尾元素(非空才能用)pop():出队(弹出队头;不返回值)
关键点:queue 不支持随机访问、也没有迭代器遍历(设计上就是只让你按队列用)。
priority_queue(优先队列 / 堆)
size():元素个数empty():是否为空push(x):插入元素(O(log n))top():查看堆顶元素(最大/最小,取决于比较器;O(1),非空才能用)pop():弹出堆顶(O(log n))- 默认:大根堆(
top()最大) - 小根堆:
priority_queue<int, vector<int>, greater<int>> q;
关键点:堆只保证“堆顶最优”,不保证整体有序。
stack(栈 LIFO)
size():元素个数empty():是否为空push(x):压栈(入栈顶)top():查看栈顶元素(非空才能用)pop():弹栈(弹出栈顶;不返回值)
常用:括号匹配、DFS 模拟递归、表达式求值。
deque(双端队列)
size():元素个数empty():是否为空clear():清空front()/back():取首/尾元素(非空才能用)push_back(x)/pop_back():尾插/尾删push_front(x)/pop_front():头插/头删begin()/end():迭代器区间[begin,end)operator[] (i):下标访问(O(1),不检查越界)
常用:单调队列优化(滑动窗口最值)、需要两端操作的场景。
set / multiset(有序集合,红黑树)
共同接口(O(log n) 为主):
size():元素个数empty():是否为空clear():清空begin()/end():迭代器区间(有序;begin()是最小)++it / --it:迭代器前进/后退(逻辑上找后继/前驱;树上实现,均摊O(1),最坏O(log n))
set(元素不重复)
insert(x):插入 x(若已存在不变)find(x):找 x,返回迭代器;找不到返回end()count(x):返回 x 出现次数(只可能 0/1)erase(x):按值删除 x(0 或 1 个)erase(it):删除迭代器指向的那个元素lower_bound(x):第一个 >= x 的迭代器upper_bound(x):第一个 > x 的迭代器
multiset(元素可重复)
count(x):可能 > 1erase(x):删除 所有 值等于 x 的元素(复杂度常写作O(k + log n),k 是删掉的个数)- 只删一个:
auto it=ms.find(x); if(it!=ms.end()) ms.erase(it);
map / multimap(有序映射,红黑树)
map(key 唯一)
insert({k,v}):插入键值对(key 已存在则插入失败/不覆盖)erase(k):按 key 删除(0/1 个)erase(it):按迭代器删除find(k):找 key,返回迭代器;否则end()operator[](k):返回 key 对应 value 的引用- 若 key 不存在,会自动插入
value的默认值(比如int默认 0) - 复杂度
O(log n)
- 若 key 不存在,会自动插入
lower_bound(k)/upper_bound(k):对 key 做“>= / >”的 bound
multimap(key 可重复)
insert({k,v})erase(it)或erase(k)(按 key 会删一组)find(k):返回某一个匹配 key 的迭代器(不保证是哪一个)equal_range(k):返回[lower_bound(k), upper_bound(k))这一段- 不支持
[](因为一个 key 可能对应多个 value)
unordered_set / unordered_map / unordered_multiset / unordered_multimap(哈希表)
共同点:
- 常见操作均摊
O(1):insert / find / erase / count - 无序:遍历顺序不固定
- 不支持
lower_bound/upper_bound,也没有“有序意义”的前驱后继
unordered_set / unordered_multiset
insert(x):插入find(x):查找count(x):set 为 0/1,multiset 可能 >1erase(x):删除(multiset 可能删多个)
unordered_map / unordered_multimap
insert({k,v})find(k)erase(k)/erase(it)operator[](k):只有 unordered_map 支持;不存在会插入默认值
(unordered_multimap 同样不支持[])
常见坑:哈希在极端数据可能退化;比赛里偶尔会被“卡哈希”,工程里一般问题不大。
bitset(定长位集)
operator[](k):访问第 k 位(可读可写)count():1 的个数any():是否至少有一个 1none():是否全为 0set():所有位设为 1set(k, v):第 k 位设为 v(v 为 0/1)reset():所有位设为 0flip():所有位取反(等价~)flip(k):第 k 位取反- 位运算:
~ & | ^ << >> - 比较:
== !=
关键点:
bitset<N>的 N 是编译期常量(不能运行时决定)。
2. vector(变长数组,倍增思想)
核心特点
- 连续内存,支持下标
[]随机访问O(1) - 扩容常用“倍增”:push 很快(均摊
O(1)) - 支持字典序比较:
vector<int>{1,2} < vector<int>{1,3}为真
常用接口
size():当前元素个数(O(1))empty():是否为空(等价size()==0)clear():清空所有元素(size变 0;容量capacity一般不一定变小)front():第一个元素的引用(非空才能用)back():最后一个元素的引用(非空才能用)push_back(x):尾部插入元素(均摊O(1),扩容时可能O(n))pop_back():删除尾部元素(非空才能用)begin()/end():返回迭代器区间[begin, end)(end 指向“尾后”)operator[] (i):下标访问第 i 个元素(O(1),不做越界检查)比较运算(<,==,...):按字典序比较(先比第 0 个,不同就决定;否则继续)
常见坑:
front/back/pop_back对空容器是未定义行为;[]越界也不会报错。
例子:基本操作 + 遍历 + 比较
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> a;
cout << a.empty() << "\n"; // empty返回bool值:1
a.push_back(10);
a.push_back(20);
a.push_back(30);
cout << a.size() << "\n"; // 3
cout << a.front() << " " << a.back() << "\n"; // 10 30
// 下标访问
cout << a[1] << "\n"; // 20
// 迭代器遍历
for (auto it = a.begin(); it != a.end(); ++it) cout << *it << " ";
cout << "\n";
// 范围for
for (int x : a) cout << x << " ";
cout << "\n";
a.pop_back(); // 删除末尾
cout << a.back() << "\n"; // 20
vector<int> b = {10, 20};
cout << (b == a) << "\n"; // 1(字典序/比较运算支持)
a.clear();
cout << a.size() << "\n"; // 0
}
3. pair<int,int> (二元组)
核心特点
-
二元组:
first、second -
支持比较:先比
first,再比second(字典序) -
first:第一个成员 -
second:第二个成员 -
比较运算:字典序比较(先比first,再比second)
用途:坐标、边 (u,v)、(value, id)、map/set 里当键都很常见。
例子:排序/比较
#include <bits/stdc++.h>
using namespace std;
int main() {
pair<int,int> p = {3, 5};
cout << p.first << " " << p.second << "\n";
vector<pair<int,int>> v = {{2,9},{1,7},{2,3},{1,2}};
sort(v.begin(), v.end()); // 按 first,再按 second
for (auto [x,y] : v) cout << "(" << x << "," << y << ") ";
cout << "\n";
// make_pair 是 C++ 标准库提供的模板函数,作用是快速创建一个 std::pair 类型的键值对对象
cout << (make_pair(1,10) < make_pair(2,0)) << "\n"; // 1
cout << (make_pair(2,3) < make_pair(2,4)) << "\n"; // 1
}
4. string(字符串)
核心特点
本质是字符序列(动态数组)
size()/length():字符串长度(等价,O(1))empty():是否为空clear():清空字符串,变成""substr(pos, len):从pos开始取子串,长度len(len省略则到末尾)s.substr(pos):从索引pos开始,截取到字符串末尾的所有字符(len省略时,默认取到最后)。-
- 若
pos超过长度会抛异常(std::out_of_range)
- 若
c_str():返回底层 C 风格字符串指针const char*,以\0结尾-
- 指针在字符串被修改后可能失效(如
push_back/+=/clear等)
- 指针在字符串被修改后可能失效(如
例子:常用操作
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "hello world";
cout << s.size() << " " << s.length() << "\n"; // 11 11
cout << s.empty() << "\n"; // 0
cout << s.substr(0, 5) << "\n"; // hello
cout << s.substr(6) << "\n"; // world
const char* p = s.c_str(); // 将string转换为const char*(C风格字符串)
cout << p << "\n";
s.clear();
cout << s.empty() << "\n"; // 1
}
| 特性 | C++ string |
C 风格字符串(const char*/char*) |
|---|---|---|
| 本质 | 类(std::string),封装字符数组 |
字符指针,指向以 \0 结尾的字符数组 |
| 内存管理 | 自动管理(无需手动申请 / 释放) | 手动管理(malloc/free/new/delete) |
| 长度获取 | size()/length() 方法(高效) |
strlen() 函数(遍历到 \0,低效) |
| 判空 | empty() 方法 |
需判断 str[0] == '\0' 或 strlen(str) == 0 |
| 拼接 / 修改 | 直接用 +/+=,或 append() 方法 |
需用 strcat()/strcpy() 等函数,易越界 |
| 安全性 | 高(自动检查边界,避免越界) | 低(手动操作易缓冲区溢出) |
| 可变性 | 非 const 的 string 可直接修改 |
const char* 不可修改,char* 可修改但风险高 |
| 结尾标志 | 内部维护长度,\0 是兼容 C 的冗余标志 |
必须以 \0 结尾,否则函数(如 strlen)会出错 |
5. queue队列
核心特点
- 只能从队尾
push,从队头pop - 访问队头
front(),队尾back() size():元素个数empty():是否为空push(x):入队(从队尾插入)front():查看队头元素(非空才能用)back():查看队尾元素(非空才能用)pop():出队(弹出队头;不返回值)
关键点:queue 不支持随机访问、也没有迭代器遍历(设计上就是只让你按队列用)。
例子:模拟排队
#include <bits/stdc++.h>
using namespace std;
int main() {
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
cout << q.front() << " " << q.back() << "\n"; // 1 3
while (!q.empty()) {
cout << q.front() << " ";
q.pop();
}
cout << "\n";
}
6. priority_queue(优先队列:堆)
核心特点
- 默认 大根堆:
top()最大 push/pop:O(log n)- 小根堆写法:
priority_queue<int, vector<int>, greater<int>> size():元素个数empty():是否为空push(x):插入元素(O(log n))top():查看堆顶元素(最大/最小,取决于比较器;O(1),非空才能用)pop():弹出堆顶(O(log n))
关键点:堆只保证“堆顶最优”,不保证整体有序。
例子:大根堆 vs 小根堆
#include <bits/stdc++.h>
using namespace std;
int main() {
priority_queue<int> big;
big.push(3); big.push(1); big.push(5);
cout << big.top() << "\n"; // 5
priority_queue<int, vector<int>, greater<int>> small;
small.push(3); small.push(1); small.push(5);
cout << small.top() << "\n"; // 1
while (!small.empty()) {
cout << small.top() << " ";
small.pop();
}
cout << "\n";
}
7. stack 栈
核心特点
- 只能操作栈顶:
push/top/pop size():元素个数empty():是否为空push(x):压栈(入栈顶)top():查看栈顶元素(非空才能用)pop():弹栈(弹出栈顶;不返回值)
常用:括号匹配、DFS 模拟递归、表达式求值。
例子:括号匹配(经典)
#include <bits/stdc++.h>
using namespace std;
bool ok(string s) {
stack<char> st;
for (char c : s) {
if (c=='(' || c=='[' || c=='{') st.push(c);
else {
if (st.empty()) return false;
char t = st.top(); st.pop();
if ((c==')' && t!='(') || (c==']' && t!='[') || (c=='}' && t!='{')) return false;
}
}
return st.empty();
}
int main() {
cout << ok("([]{})") << "\n"; // 1
cout << ok("([)]") << "\n"; // 0
}
8. deque(双端队列)
核心特点
- 两端都能
push/pop - 也支持
[]随机访问(但实现非纯连续) size():元素个数empty():是否为空clear():清空front()/back():取首/尾元素(非空才能用)push_back(x)/pop_back():尾插/尾删push_front(x)/pop_front():头插/头删begin()/end():迭代器区间[begin,end)operator[] (i):下标访问(O(1),不检查越界)
常用:单调队列优化(滑动窗口最值)、需要两端操作的场景。
例子:双端进出 + 下标访问
#include <bits/stdc++.h>
using namespace std;
int main() {
deque<int> d;
d.push_back(2);
d.push_front(1);
d.push_back(3);
cout << d.front() << " " << d.back() << "\n"; // 1 3
cout << d[1] << "\n"; // 2
d.pop_front(); // 删 1
d.pop_back(); // 删 3
for (int x : d) cout << x << " ";
cout << "\n"; // 2
}
9. set / map / multiset / multimap(红黑树,有序,O(log n))
共同特点
- 自动有序(默认升序,按
<) begin()最小元素迭代器,--end()最大++it / --it找后继/前驱(本质树上走,O(log n))lower_bound/upper_bound:二分含义(树上实现)
9.1 set(不重复)
常用
insert(x)find(x):找到返回迭代器,否则end()count(x):只有 0/1erase(x):按值删erase(it):按迭代器删一个
例子:查找 + 前驱后继 + bound
#include <bits/stdc++.h>
using namespace std;
int main() {
set<int> s;
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3); // 不会重复
for (int x : s) cout << x << " ";
cout << "\n"; // 1 3 5
auto it = s.find(3);
if (it != s.end()) {
cout << *it << "\n"; // 3
// 前驱
if (it != s.begin()) {
auto pre = prev(it);
cout << "pre=" << *pre << "\n"; // 1
}
// 后继
auto nxt = next(it);
if (nxt != s.end()) cout << "next=" << *nxt << "\n"; // 5
}
cout << *s.lower_bound(4) << "\n"; // 5(>=4 的最小)
cout << *s.upper_bound(3) << "\n"; // 5(>3 的最小)
s.erase(1);
cout << s.count(1) << "\n"; // 0
}
9.2 multiset(可重复)
特点
count(x)可能 > 1erase(x)会删除所有 x(注意!)- 如果只删一个:
auto it = ms.find(x); ms.erase(it);
例子:重复元素删除策略
#include <bits/stdc++.h>
using namespace std;
int main() {
multiset<int> ms;
ms.insert(2);
ms.insert(2);
ms.insert(3);
cout << ms.count(2) << "\n"; // 2
// 只删一个2
auto it = ms.find(2);
if (it != ms.end()) ms.erase(it);
cout << ms.count(2) << "\n"; // 1
// 删所有3
ms.erase(3);
cout << ms.count(3) << "\n"; // 0
}
9.3 map(键唯一,key -> value)
核心点
- 存
pair<const Key, T> mp[key]:若 key 不存在,会自动插入默认值(很常见坑/很好用)find/erase/lower_bound/upper_bound都按 key
例子:计数(频率统计)
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> a = {1,2,1,3,2,1};
map<int,int> mp;
// mp[x] 的本质是返回「键为 x 的 pair<const int, int> 的 second 的引用」
for (int x : a) mp[x]++; // [] 自动插入0再second++
/* 以x=1为例,第一次执行mp[1]++的过程:
1. 查找键为1的元素 → 没找到;
2. 自动插入新元素:pair<const int, int>(1, 0);
3. 返回这个新元素的second(0)的引用;
4. 执行++ → second变为1;
后续x=1再出现时,直接找到键1的元素,second自增(最终变为3)。
*/
for (auto [k,v] : mp) {
cout << k << " -> " << v << "\n";
}
auto it = mp.find(2); //find返回map<int,int>::iterator 类型迭代器的,迭代器要用->
if (it != mp.end()) cout << it->first << " " << it->second << "\n";
cout << mp.lower_bound(2)->first << "\n"; // >=2 的最小key
}
9.4 multimap(键可重复,没有 [])
特点
- 一个 key 对应多个 value
- 查一段区间:
equal_range(key)或[lower_bound, upper_bound)
例子:同 key 多值
#include <bits/stdc++.h>
using namespace std;
int main() {
multimap<int,string> mm;
mm.insert({1,"A"});
mm.insert({1,"B"});
mm.insert({2,"C"});
auto [L, R] = mm.equal_range(1);
for (auto it = L; it != R; ++it) {
cout << it->first << " " << it->second << "\n";
}
}
#include <bits/stdc++.h>
using namespace std;
int main() {
map<int, int> mp = {{1, 10}, {2, 20}, {3, 30}};
// ========== 错误用法:直接操作mp.first/mp.second ==========
// cout << mp.first << endl; // 编译报错!map没有first成员
// cout << mp.second << endl; // 编译报错!map没有second成员
// ========== 正确用法:先找元素,再访问first/second ==========
// 方式1:通过迭代器访问单个元素
auto it = mp.find(2); // 找到键为2的元素(pair<const int, int>)
if (it != mp.end()) {
cout << "键(first):" << it->first << endl; // 输出 2(元素的键)
cout << "值(second):" << it->second << endl; // 输出 20(元素的值)
}
// 方式2:遍历所有元素,逐个访问first/second
cout << "\n遍历所有元素:\n";
for (auto& p : mp) { // p是map中的单个元素(pair<const int, int>)
cout << "键:" << p.first << ",值:" << p.second << endl;
}
// 方式3:通过[]访问值(second),但无法直接访问键(first)
mp[2] = 200; // 修改键2对应的second为200
cout << "\n修改后键2的值:" << mp[2] << endl; // 输出 200(仅能操作second)
return 0;
}
9.5 哪些场景能直接用 .first/.second?
1. 直接创建的 pair 对象(最基础场景)
#include <bits/stdc++.h>
using namespace std;
int main() {
// 1. 直接创建pair对象,可直接用.first/.second
pair<int, string> p = {1, "hello"};
cout << p.first << endl; // 输出 1(pair的第一个值)
cout << p.second << endl; // 输出 hello(pair的第二个值)
// 2. 用make_pair创建的pair,同样可以
auto p2 = make_pair(2, "world");
cout << p2.first << " " << p2.second << endl; // 输出 2 world
return 0;
}
2. map/unordered_map 的元素(本质是 pair)
map<K,V> 的每个元素都是 pair<const K, V>,所以遍历 / 找到元素后,能对元素(pair)用 .first/.second:
map<int, int> mp = {{1, 10}, {2, 20}};
// 遍历map,p是pair<const int, int>类型
for (auto& p : mp) {
cout << p.first << " -> " << p.second << endl; // 1->10、2->20
}
// 迭代器指向pair,用->first/->second(指针/迭代器的写法)
auto it = mp.find(1);
if (it != mp.end()) {
cout << it->first << " " << it->second << endl; // 1 10
}
3. 存放 pair 的容器(比如 vector<pair>)
如果容器里存的是 pair,那么容器的每个元素都能直接用 .first:
vector<pair<int, int>> vec = {{1,2}, {3,4}};
for (auto& p : vec) {
cout << p.first << " " << p.second << endl; // 1 2、3 4
}
三、哪些场景不能直接用 .first?(避坑)
- ❌
map/vector/list等容器本身:比如mp.first、vec.first(编译报错,容器没有这些成员); - ❌ 普通变量 / 数组:比如
int a = 1; a.first(毫无意义,编译报错); - ❌
map的[]操作结果:mp[1]是pair的second(值),不是pair本身,所以mp[1].first会报错(int没有first)。
四、关键区别:. 和 -> 的用法
新手容易混淆这两种访问方式,核心规则:
| 写法 | 适用场景 | 示例 |
|---|---|---|
.first |
直接操作 pair 对象 / 引用 |
p.first、(*it).first |
->first |
操作指向 pair 的指针 / 迭代器 |
it->first、ptr->first |
pair<int, int> p = {1,2};
pair<int, int>* ptr = &p; // 指针指向pair
cout << p.first << endl; // 正确,直接访问对象
cout << ptr->first << endl;// 正确,访问指针指向的对象
cout << (*ptr).first << endl;// 等价于ptr->first,先解引用再访问
10. unordered_set / unordered_map / unordered_multiset / unordered_multimap(哈希表)
核心特点
- 均摊
O(1)增删改查(最坏可能退化,但一般不管) - 无序:遍历顺序不保证
- 不支持
lower_bound/upper_bound,也不支持有序意义的前驱后继
例子:unordered_map 计数
#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_map<string,int> cnt;
vector<string> words = {"aa","bb","aa","cc","bb","aa"};
for (auto &w : words) cnt[w]++;
cout << cnt["aa"] << "\n"; // 3
cout << (cnt.find("dd") == cnt.end()) << "\n"; // 1
// 遍历无序
for (auto &kv : cnt) {
cout << kv.first << " -> " << kv.second << "\n";
}
}
11. bitset(压位)
核心特点
- 定长:
bitset<N>表示一个长度为N的二进制位串,N必须是编译期常量,比如bitset<8>就是 8 位二进制数。 - 支持按位运算:
~ & | ^ << >> []访问某一位(返回代理对象,可读可写)- 常用:状态压缩 DP、快速集合运算、筛法等
常用接口
count, any, none, set, set(k,v), reset, flip, flip(k)
| 接口 | 作用 | 示例 |
|---|---|---|
set(k) |
将第 k 位设为 1 |
b.set(1) → 第 1 位变成 1 |
set(k, v) |
将第 k 位设为 v(v 是 0 或 1) |
b.set(3, 1) → 第 3 位变成 1 |
reset() |
将所有位设为 0 |
b.reset() → 全 0 |
reset(k) |
将第 k 位设为 0 |
b.reset(1) → 第 1 位变成 0 |
flip() |
翻转所有位(0 变 1,1 变 0) | b.flip() → 00001010 → 11110101 |
flip(k) |
翻转第 k 位 |
b.flip(1) → 00001010 → 00001000 |
count() |
返回 1 的位数 |
b.count() → 2(00001010 有两个 1) |
any() |
是否至少有一个 1 |
b.any() → 1(true) |
none() |
是否全为 0 |
b.none() → 0(false) |
[] |
访问第 k 位(可读可写) |
b[0] → 第 0 位的值 |
例子:基本位操作
#include <bits/stdc++.h>
using namespace std;
int main() {
bitset<8> b; // 00000000
b.set(1); // 00000010
/*
位编号:7 6 5 4 3 2 1 0
数值: 0 0 0 0 0 0 1 0 → 对应十进制 2
*/
b.set(3); // 00001010
cout << b << "\n";
cout << b.count() << "\n"; // 2
cout << b.any() << "\n"; // 1
cout << b.none() << "\n"; // 0
b.flip(1); // 00001000
cout << b << "\n";
b.reset(); // 00000000
cout << b << "\n";
b.set(); // 11111111
cout << b << "\n";
bitset<8> c(string("00110011"));
// 创建一个长度为 8 的 bitset 对象 c,并按照字符串 "00110011" 的内容,给 c 的每一位赋值。
cout << (b & c) << "\n"; // 与
cout << (b ^ c) << "\n"; // 异或
cout << (c << 2) << "\n"; // 左移
}
12. 范围for
- 范围 for(range-based for)是什么
语法:
for (声明 : 容器/范围) { ... }
它等价于用 begin() / end() 去遍历(本质就是迭代器循环),适用于能提供 begin/end 的“可迭代对象”:大多数 STL 容器、string、数组、initializer_list、map/set 等。
- 三种最常见写法(很重要)
A. 按值遍历(拷贝)
for (auto x : v) { /* x 是元素的拷贝 */ }
- 改
x不会影响容器 - 元素大(比如
string、pair、自定义结构体)会有拷贝开销
B. 按引用遍历(可修改)
for (auto &x : v) { /* x 是元素引用,可改容器内元素 */ }
C. 按 const 引用遍历(只读,推荐默认)
for (const auto &x : v) { /* 不拷贝、也不修改 */ }
- 最常用:既省拷贝又安全
- 常见 STL 用范围 for 怎么写
3.1 vector / deque(元素序列)
vector<int> v = {1,2,3};
for (int x : v) cout << x << " "; // 按值
for (auto &x : v) x *= 2; // 修改
for (const auto &x : v) cout << x << " "; // 只读
3.2 string(字符序列)
string s = "abc";
for (char c : s) cout << c << "\n";
for (auto &c : s) c = toupper(c); // 修改字符
3.3 set / multiset(有序集合:遍历得到“元素值”)
set<int> st = {3,1,2};
for (int x : st) cout << x << " "; // 1 2 3(自动有序)
注意:
set里的元素是“只读”的(key 不允许被改),所以别写auto &x去修改。
3.4 unordered_set / unordered_multiset(无序集合)
unordered_set<int> us = {3,1,2};
for (int x : us) cout << x << " "; // 顺序不保证
3.5 map / unordered_map(遍历得到的是 pair<const K, V>)
方式 1:用结构化绑定(最舒服)
map<int,string> mp = {{2,"b"},{1,"a"}};
for (const auto &[k, v] : mp) {
cout << k << " " << v << "\n";
}
方式 2:用 pair
for (const auto &kv : mp) {
cout << kv.first << " " << kv.second << "\n";
}
k是const(不能改 key),但v通常可以改(如果用引用拿到)。
3.6 multimap / unordered_multimap(同 map,用法一致)
multimap<int,int> mm;
mm.insert({1,10});
mm.insert({1,20});
for (const auto &[k,v] : mm) cout << k << ":" << v << "\n";
3.7 pair 本身不是“容器”,不能直接 range-for
但经常在遍历 map 时得到 pair。
3.8 queue / stack / priority_queue:不能用范围 for
原因:它们是容器适配器,不提供迭代器(没有 begin/end)。
正确做法:while(!empty()) { top/front; pop; }
queue<int> q;
while (!q.empty()) {
cout << q.front() << " ";
q.pop();
}
3.9 bitset:通常不直接 range-for(没有标准 begin/end)
常用写法是下标循环:
bitset<8> b(string("10110000"));
for (int i = 0; i < 8; i++) cout << b[i]; // b[i] 访问第 i 位
也可以
cout << b;直接输出整串。
4) 一句话速记:哪些能 range-for?
✅ vector/deque/string/set/map/unordered_* 都可以
❌ queue/stack/priority_queue 不行(用 while+pop)
⚠️ bitset 通常用下标循环(或直接输出)
更多推荐

所有评论(0)