很好!你想深入 STL(Standard Template Library)和容器算法,是学习 C++ 非常关键的一步。下面我给你整理一份STL 容器 + 算法的入门指南,适合从零起步掌握这部分内容。
🌟 一、STL 核心模块
STL 分为三大块,你主要先掌握前两块:
- 容器(Containers) 👉 存放数据的结构
- 算法(Algorithms) 👉 操作容器里的数据
- 迭代器(Iterators)👉 算法和容器的桥梁
📦 二、常用容器分类 + 用法示例
类型 | 容器 | 特点 |
---|---|---|
顺序容器 | vector , list , deque | 有序访问,插入删除位置有限制 |
关联容器 | set , map | 自动排序,基于红黑树 |
无序容器 | unordered_set , unordered_map | 基于哈希表,查找快但无序 |
适配器 | stack , queue , priority_queue | 封装其他容器提供特定操作 |
例子:使用 vector
#include <vector>
#include <iostream>
using namespace std;int main() {vector<int> v = {1, 2, 3};v.push_back(4); // 添加元素v.pop_back(); // 删除末尾cout << v[0] << endl; // 访问元素
}
🧠 三、常用算法(头文件 <algorithm>
)
STL 提供了 80 多种常用算法,先学这些最基础的:
分类 | 代表函数 | 用法示例 |
---|---|---|
排序 | sort(v.begin(), v.end()) | 排序 vector |
查找 | find , count , binary_search | 判断是否存在某个元素 |
遍历 | for_each , transform | 应用函数到每个元素 |
修改 | reverse , replace , fill | 反转、替换、填充 |
例子:排序 + 查找
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;int main() {vector<int> v = {3, 1, 4, 2};sort(v.begin(), v.end()); // 排序if (binary_search(v.begin(), v.end(), 3))cout << "Found 3" << endl;
}
🔄 四、迭代器的用法(配合容器+算法)
所有容器都有 .begin()
和 .end()
返回迭代器。
vector<int> v = {1, 2, 3};
for (auto it = v.begin(); it != v.end(); ++it)cout << *it << " ";
🛠 五、推荐学习顺序
- 掌握
vector
,set
,map
的基本用法 - 学会使用
sort
,find
,count
,reverse
等常见算法 - 理解迭代器是怎么配合算法工作的
- 学会写
lambda
来配合sort
、for_each
- 开始写简单的项目/功能:统计词频、模拟排行榜、实现任务队列
好!下面是针对每类容器精心设计的小练习题 + 示例代码,每题只用一种容器,帮助你理解用法和适用场景:
🧱 1. 顺序容器练习题:vector
📌 题目:输入一组整数,输出其中所有大于5的数,按升序排列
✅ 示例代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {vector<int> nums = {3, 7, 1, 9, 5, 6};vector<int> result;for (int x : nums)if (x > 5) result.push_back(x);sort(result.begin(), result.end()); // 升序排序for (int x : result)cout << x << " "; // 输出:6 7 9
}
🌲 2. 关联容器练习题:set
📌 题目:输入一组数字,输出去重后的升序结果
✅ 示例代码:
#include <iostream>
#include <set>
using namespace std;int main() {set<int> s;int a[] = {5, 1, 2, 2, 3, 5, 4};for (int x : a)s.insert(x); // 自动排序+去重for (int x : s)cout << x << " "; // 输出:1 2 3 4 5
}
⚡ 3. 无序容器练习题:unordered_map
📌 题目:统计字符串中每个字符的出现次数(频次统计)
✅ 示例代码:
#include <iostream>
#include <unordered_map>
using namespace std;int main() {string s = "abracadabra";unordered_map<char, int> freq;for (char c : s)freq[c]++;for (auto& p : freq)cout << p.first << ": " << p.second << endl;// 示例输出(顺序不固定):// a: 5// b: 2// r: 2// c: 1// d: 1
}
🎒 4. 容器适配器练习题:stack
📌 题目:检查一个括号字符串是否左右匹配
例如 "(()())"
匹配,"(()"
不匹配。
✅ 示例代码:
#include <iostream>
#include <stack>
using namespace std;bool isValid(string s) {stack<char> stk;for (char c : s) {if (c == '(') stk.push(c);else if (c == ')') {if (stk.empty()) return false;stk.pop();}}return stk.empty();
}int main() {string test = "(()())";cout << (isValid(test) ? "匹配" : "不匹配") << endl;
}
✅ 总结练习与容器匹配
容器 | 小题练习 | 关键词 |
---|---|---|
vector | 筛选+排序 | 线性数据 |
set | 去重+排序 | 唯一集合 |
unordered_map | 统计频率 | 快速查找 |
stack | 括号匹配 | 后进先出 |
下面按 四大类 + 每类具体容器 给出简单易懂的例子和对应代码,帮助你快速掌握它们的用法。
1. 顺序容器(Sequence Containers)
1.1 vector
场景:存一组学生分数,按索引随机访问
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> scores = {85, 90, 78};scores.push_back(92); // 在末尾添加一个分数cout << "第三个学生的分数是:" << scores[2] << endl; // 随机访问return 0;
}
1.2 list
场景:存一条火车车厢编号,频繁在中间插入/删除
#include <iostream>
#include <list>
using namespace std;int main() {list<string> train = {"Car1", "Car3"};auto it = train.begin();++it; // 指向 "Car3"train.insert(it, "Car2"); // 在中间插入for (auto& c : train)cout << c << " "; // 输出:Car1 Car2 Car3 return 0;
}
1.3 deque
场景:实现一个简单的滑动窗口
#include <iostream>
#include <deque>
using namespace std;int main() {deque<int> window;int nums[] = {1,2,3,4,5};for (int x : nums) {window.push_back(x); // 新元素入队if (window.size() > 3) // 窗口大小保持 3window.pop_front(); // 弹出最旧元素cout << "当前窗口:";for (int y : window) cout << y;cout << endl;}return 0;
}
2. 关联容器(Associative Containers)
2.1 set
场景:自动去重并升序存储数字
#include <iostream>
#include <set>
using namespace std;int main() {set<int> s = {4, 2, 5, 2, 3};// 重复的 2 会自动去掉,元素自动排序cout << "排序后的唯一元素:";for (int x : s) cout << x << " "; // 输出:2 3 4 5return 0;
}
2.2 map
场景:模拟电话簿,用姓名查电话
#include <iostream>
#include <map>
using namespace std;int main() {map<string, string> phonebook;phonebook["Alice"] = "123-456";phonebook["Bob"] = "987-654";cout << "Alice 的电话是:" << phonebook["Alice"] << endl;return 0;
}
3. 无序容器(Unordered Containers)
3.1 unordered_set
场景:快速检测某元素是否出现过
#include <iostream>
#include <unordered_set>
using namespace std;int main() {unordered_set<string> seen;string tokens[] = {"apple","banana","apple"};for (auto& t : tokens) {if (seen.count(t))cout << t << " 已出现过\n";else {cout << t << " 首次出现\n";seen.insert(t);}}return 0;
}
3.2 unordered_map
场景:统计字符串中每个字符的出现次数
#include <iostream>
#include <unordered_map>
using namespace std;int main() {string s = "banana";unordered_map<char,int> cnt;for (char c : s)cnt[c]++;for (auto& p : cnt)cout << p.first << " 出现了 " << p.second << " 次\n";return 0;
}
4. 容器适配器(Container Adapters)
4.1 stack
场景:检查括号匹配
#include <iostream>
#include <stack>
using namespace std;bool isValid(string s) {stack<char> st;for (char c : s) {if (c == '(') st.push(c);else if (c == ')') {if (st.empty()) return false;st.pop();}}return st.empty();
}int main() {cout << (isValid("(()())") ? "匹配" : "不匹配") << endl;return 0;
}
4.2 queue
场景:打印机任务队列,先进先出
#include <iostream>
#include <queue>
using namespace std;int main() {queue<string> jobs;jobs.push("doc1"); jobs.push("doc2");while (!jobs.empty()) {cout << "打印 " << jobs.front() << endl;jobs.pop();}return 0;
}
4.3 priority_queue
场景:任务调度,优先级高的先执行
#include <iostream>
#include <queue>
using namespace std;int main() {// 默认最大堆priority_queue<pair<int,string>> pq;pq.push({1,"低优先"}); pq.push({5,"高优先"});pq.push({3,"中优先"});while (!pq.empty()) {cout << pq.top().second << endl;pq.pop();}// 输出:高优先 → 中优先 → 低优先return 0;
}
区分 set
, multiset
, map
, multimap
1. set
- 特点:自动排序、去重
- 典型场景:提取一段文本中的不重复单词并按字母序输出
#include <iostream>
#include <set>
#include <sstream>
#include <string>
using namespace std;int main() {string text = "apple orange banana apple pear banana";istringstream iss(text);set<string> s;string w;while (iss >> w) {s.insert(w); // 重复单词只保留一个,自动按字母序排序}cout << "不重复单词(按序):";for (const auto& word : s) {cout << word << " ";}// 输出:banana apple orange pearreturn 0;
}
2. multiset
- 特点:自动排序、允许重复
- 典型场景:统计一组成绩的分布(但还想保留原始次数顺序)
#include <iostream>
#include <set>
using namespace std;int main() {multiset<int> scores = {85, 90, 78, 90, 85, 92};// 自动排序,但保留重复值cout << "所有成绩(升序,含重复):";for (int sc : scores) {cout << sc << " ";}// 输出:78 85 85 90 90 92return 0;
}
3. map
- 特点:键唯一、自动排序 key → value
- 典型场景:统计单词出现频率
#include <iostream>
#include <map>
#include <sstream>
#include <string>
using namespace std;int main() {string text = "apple banana apple pear banana apple";istringstream iss(text);map<string,int> freq;string w;while (iss >> w) {freq[w]++; // key 不存在时自动插入,value 初始为 0}cout << "单词频率:\n";for (auto& p : freq) {cout << p.first << " -> " << p.second << "\n";}// 按字母序输出:// apple -> 3// banana -> 2// pear -> 1return 0;
}
4. multimap
- 特点:允许重复 key → value(一个 key 可对应多个 value)
- 典型场景:一个人选修多门课程
#include <iostream>
#include <map>
#include <string>
using namespace std;int main() {multimap<string, string> courses;courses.insert({"Alice", "Math"});courses.insert({"Bob", "Physics"});courses.insert({"Alice", "English"});courses.insert({"Bob", "Chemistry"});// 遍历所有同学及其课程cout << "选课列表:\n";for (auto& p : courses) {cout << p.first << " 选修 " << p.second << "\n";}// 如果只想看 Alice 的课程:cout << "\nAlice 的课程:\n";auto range = courses.equal_range("Alice");for (auto it = range.first; it != range.second; ++it) {cout << it->second << "\n";}// 输出:// Math// Englishreturn 0;
}
通过这四个例子,你可以看到:
set
:帮你去重并排序multiset
:排序但保留重复map
:唯一 key → 单一 valuemultimap
:同一个 key 下可有多个 value
区分 unordered_set, unordered_multiset, unordered_map, unordered_multimap
好的,我把每个容器的例子都写得更详细一些,涵盖常用操作(插入、查找、删除、遍历等),并加上注释说明。
1. unordered_set
(无序不重复集合)
场景:记录一段日志里出现过的错误代码,只关心是否出现过
#include <iostream>
#include <unordered_set>
using namespace std;int main() {// 准备一段“错误码”序列int errors[] = {101, 202, 303, 101, 404, 202, 505};// 定义 unordered_set 容器,记录首次出现的错误码unordered_set<int> seen;// 遍历所有错误码for (int code : errors) {// count 返回 0 或 1,表示容器里是否已有该元素if (seen.count(code)) {cout << "错误码 " << code << " 已记录过,跳过\n";} else {cout << "首次出现错误码 " << code << ",记录\n";seen.insert(code); // 插入新元素}}cout << "\n最终记录的错误码有 " << seen.size() << " 个:";for (auto c : seen) // 无序遍历cout << c << " ";cout << endl;// 删除某个错误码int to_remove = 303;if (seen.erase(to_remove)) // erase 返回删除的元素数量cout << "已删除错误码 " << to_remove << "\n";return 0;
}
要点
- 插入:
insert(val)
- 查找:
count(key)
或find(key)
- 删除:
erase(key)
- 遍历:范围 for(顺序不固定)
2. unordered_multiset
(无序可重复集合)
场景:统计商品售出记录中各商品的销售次数
#include <iostream>
#include <unordered_set>
using namespace std;int main() {// 假设售出商品 ID 列表int sales[] = {1001,1002,1001,1003,1002,1001,1004};// unordered_multiset 允许重复unordered_multiset<int> bag;// 插入所有售出记录for (int id : sales) {bag.insert(id);}// 想知道某个商品卖了多少次int query = 1001;cout << "商品 " << query << " 共售出 "<< bag.count(query) << " 次。\n";// 遍历所有记录(会重复显示)cout << "所有售出商品 ID(包含重复):";for (int id : bag)cout << id << " ";cout << endl;// 删除一次售出记录(只删除一个实例)auto it = bag.find(query);if (it != bag.end()) {bag.erase(it);cout << "删除一次 " << query << " 的记录后,剩余 "<< bag.count(query) << " 次。\n";}return 0;
}
要点
- 插入:
insert(val)
- 统计:
count(key)
- 查找一个实例:
find(key)
- 删除单个实例:
erase(iterator)
或erase(key)
(后者会删掉所有实例)
3. unordered_map
(无序键→值映射)
场景:统计一段文字里每个单词出现的次数,并支持查询、删除
#include <iostream>
#include <unordered_map>
#include <sstream>
using namespace std;int main() {string text = "apple orange banana apple pear banana";istringstream iss(text);// 定义字符到出现次数的映射unordered_map<string,int> freq;// 统计词频string w;while (iss >> w) {freq[w]++; // 如果 key 不存在,会自动插入并初始化为 0}// 输出所有词频cout << "词频统计:\n";for (auto& p : freq) {cout << p.first << " -> " << p.second << "\n";}// 查询某个单词string query = "banana";auto it = freq.find(query);if (it != freq.end()) {cout << query << " 出现了 " << it->second << " 次\n";} else {cout << query << " 没出现过\n";}// 删除某个单词的统计freq.erase("pear");cout << "删除 pear 后,剩余 " << freq.size() << " 个单词记录。\n";return 0;
}
要点
- 插入/修改:
operator[]
或emplace(key,val)
- 查找:
find(key)
- 删除:
erase(key)
或erase(iterator)
- 遍历:
for (auto& p : map)
4. unordered_multimap
(无序键→多值映射)
场景:一个作者可能有多本书,要存储“作者 → 书名”并支持查找、遍历
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;int main() {// 定义作者到书名的多映射unordered_multimap<string,string> library;library.emplace("Alice", "C++ Primer");library.emplace("Bob", "Effective C++");library.emplace("Alice", "STL 源码剖析");library.emplace("Bob", "现代 C++ 设计");// 遍历全部条目cout << "馆藏列表:\n";for (auto& p : library) {cout << p.first << " 著《" << p.second << "》\n";}// 查找某位作者的所有书string author = "Alice";cout << "\n查询 " << author << " 的作品:\n";auto range = library.equal_range(author);for (auto it = range.first; it != range.second; ++it) {cout << it->second << "\n";}// 删除某本特定书(必须先找到对应 pair 的 iterator)for (auto it = range.first; it != range.second; ++it) {if (it->second == "C++ Primer") {library.erase(it);cout << "\n已删除 Alice 的《C++ Primer》\n";break;}}return 0;
}
要点
- 插入:
emplace(key,val)
或insert({key,val})
- 查找所有值:
equal_range(key)
返回[first, second)
- 删除单条记录:
erase(iterator)
分别介绍这八种关联容器
一、有序关联容器(红黑树,所有操作 O(log n))
1. std::set<Key, Compare, Alloc>
- 键唯一,自动按
Compare
排序(默认<
)。 - 典型接口
insert(k)
/emplace(k)
:插入元素erase(it)
/erase(key)
:按迭代器或键删除find(key)
:查找,返回迭代器或end()
count(key)
:键存在返回 1,否则 0- 范围查询:
lower_bound(key)
、upper_bound(key)
、equal_range(key)
- 遍历保证从小到大。
#include <iostream>
#include <set>
using namespace std;int main() {set<int> s;// 插入s.insert(3);s.insert(1);s.emplace(2); // 总共 {1,2,3}// 查找if (s.find(2) != s.end())cout << "Found 2\n";// 遍历cout << "遍历 set:";for (int x : s) cout << x << ' '; // 1 2 3cout << endl;// 下界、上界auto lb = s.lower_bound(2); // 指向 2auto ub = s.upper_bound(2); // 指向 3// 删除s.erase(1); // 删除键为1的元素cout << "删除后大小:" << s.size() << endl;
}
2. std::multiset<Key, Compare, Alloc>
- 允许重复键,其他与
set
相同。 - 插入不会合并相同键;
- 区间操作用
equal_range(key)
返回[first, second)
。
#include <iostream>
#include <set>
using namespace std;int main() {multiset<string> ms;ms.insert("apple");ms.insert("banana");ms.insert("apple"); // 允许重复// 查看所有 "apple"auto range = ms.equal_range("apple");cout << "\"apple\" 出现次数:";for (auto it = range.first; it != range.second; ++it)cout << *it << ' ';cout << endl; // apple apple// 删除所有 "apple"ms.erase(range.first, range.second);cout << "删除后大小:" << ms.size() << endl;
}
3. std::map<Key, T, Compare, Alloc>
- 键唯一,每个键对应一个值。
- 访问
operator[](key)
:不存在则插入默认值并返回引用at(key)
:不存在抛out_of_range
- 查找/删除同
set
。 - 遍历按键升序,访问
pair<const Key,T>
。
#include <iostream>
#include <map>
using namespace std;int main() {map<int, string> m;m[1] = "one"; // 插入或修改m.insert({2, "two"}); // 插入// m[3] 默认插入 "",再赋值m[3] = "three";// 查找auto it = m.find(2);if (it != m.end())cout << "key=2, value=" << it->second << endl;// 范围遍历cout << "遍历 map:\n";for (auto& [k,v] : m)cout << k << " → " << v << "\n";// 区间删除 1 ≤ key < 3auto it1 = m.lower_bound(1);auto it3 = m.lower_bound(3);m.erase(it1, it3);cout << "删除区间后大小:" << m.size() << endl;
}
4. std::multimap<Key, T, Compare, Alloc>
- 允许重复键,其他同
map
。 - 取相同键所有值用
equal_range(key)
。
#include <iostream>
#include <map>
using namespace std;int main() {multimap<int, string> mm;mm.emplace(1, "uno");mm.emplace(1, "ichi");mm.emplace(2, "dos");auto range = mm.equal_range(1);cout << "key=1 的所有值:";for (auto it = range.first; it != range.second; ++it)cout << it->second << ' ';cout << endl;// 删除其中一个 pairauto it = range.first;mm.erase(it);cout << "删除一个后剩余:" << mm.count(1) << " 个\n";
}
二、无序关联容器(哈希表,平均 O(1),最坏 O(n))
1. std::unordered_set<Key, Hash, KeyEqual, Alloc>
- 键唯一,底层是哈希桶+链表/开放寻址。
- 常用接口
reserve(n)
:预留至少能放下 n 个元素max_load_factor(f)
:设置最大负载因子bucket_count()
、load_factor()
:查看当前桶数和负载
- 查找/插入/删除平均 O(1)。
#include <iostream>
#include <unordered_set>
using namespace std;int main() {unordered_set<int> us;us.reserve(100); // 至少 100 个桶us.insert({3,1,4,1,5}); // 重复的1会被忽略cout << "元素:";for (int x : us) cout << x << ' '; // 无序输出cout << endl;cout << "count(1)=" << us.count(1) << endl; // 1 或 0us.erase(3);cout << "erase后 size=" << us.size() << endl;
}
2. std::unordered_multiset<Key, Hash, KeyEqual, Alloc>
- 允许重复键,其他同
unordered_set
。 - 获取所有相同键用
equal_range(key)
。
#include <iostream>
#include <unordered_set>
using namespace std;int main() {unordered_multiset<int> ums;ums.insert(2);ums.insert(2);ums.insert(3);auto range = ums.equal_range(2);cout << "2 出现次数:";for (auto it = range.first; it != range.second; ++it)cout << *it << ' ';cout << endl;
}
3. std::unordered_map<Key, T, Hash, KeyEqual, Alloc>
- 键唯一,哈希字典。
- 访问同
map
:operator[]
、at()
。 - reserve、rehash 可控制性能。
#include <iostream>
#include <unordered_map>
using namespace std;int main() {unordered_map<string,int> um;um.reserve(50);um["apple"] = 3;um.emplace("banana", 2);cout << "apple 个数:" << um["apple"] << endl;if (um.find("cherry")==um.end())cout << "no cherry\n";// 自定义哈希和相等struct Point { int x,y; };struct PointHash { size_t operator()(Point const& p) const noexcept {return p.x*31 + p.y;}};struct PointEq { bool operator()(Point const& a, Point const& b) const noexcept {return a.x==b.x && a.y==b.y;}};unordered_map<Point,string,PointHash,PointEq> mp;mp[{1,2}] = "A";cout << mp[{1,2}] << endl;
}
4. std::unordered_multimap<Key, T, Hash, KeyEqual, Alloc>
- 允许重复键,其他同
unordered_map
。 - 取所有同键用
equal_range
。
#include <iostream>
#include <unordered_map>
using namespace std;int main() {unordered_multimap<int,string> umm;umm.emplace(1,"a");umm.emplace(1,"b");umm.emplace(2,"c");for (auto it = umm.equal_range(1).first; it != umm.equal_range(1).second; ++it)cout << it->second << ' ';cout << endl;
}
三、选型建议
- 需要排序/范围查询 → 选有序(
set/map
)。 - 追求最快查插且无需排序 → 选无序(
unordered_*
)。 - 是否允许重复键 → 普通版 vs.
multi
版。 - 自定义排序/哈希 → 提供
Compare
或Hash/KeyEqual
。