很好!你想深入 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
。