浅谈C++ 容器查找效率
目录
- 0. 先把“查找复杂度”聊明白
- 1. std::vector —— “排排坐”的长桌子
- 2. std::list —— “珍珠项链”
- 3. std::set —— 自动排序的“有序字典”
- 4. std::unordered_set —— “散列表”小黑屋
- 5. std::map vs std::unordered_map —— 键值对“双子星”
- 6. std::deque —— 可以从两头装菜的“长盘子”
- 7. “多索引&rphpdquo;技巧 —— 一份数据,多把钥匙
- 8. 记忆卡片(纯中文朗朗上口)
- 最后一句话
只要选对容器,多写几行代码就能让程序“飞”起来。下面用生活化的比喻 + 足够多的带注释示例,帮你弄懂常用 STL 容器的查找特性。
读完你应该能快速判断:“我的场景该用哪一个?”0. 先把“查找复杂度”聊明白
记号 | 含义 | 举个 |
---|---|---|
O(1) | 常数时间 | 像从冰箱门口拿饮料——一下就到手 |
O(log n) | 对数时间 | 折半查电话簿——越找越快 |
O(n) | 线性时间 | 排队买奶茶——要挨个等 |
只要记住:数字越小越快,O(1)
最爽,O(n)
最慢。
1. stdjavascript::vector —— “排排坐”的长桌子
特点:元素一字排开,内存连续,跟操场跑道一样直。
查找
- 没排序:只能一个个比——
O(n)
- 排好序:可折半查——
O(log n)
#include <vector> #include <algorithm> // std::sort, std::binary_search #include <IOStream> int main() { std::vector<int> nums{5, 1, 4, 3, 2}; // 连续摆放的「长桌子」 std::sort(nums.begin(), nums.end()); // ① 排序一次,之后查找快 // nums 变成 {1,2,3,4,5} bool has3 = std::binary_search( // ② 折半查找 3 nums.begin(), nums.end(), 3); // O(log n) std::cout << (has3 ? "找到了" : "没找到") << '\n'; }
什么时候用
- 数据量可控、想要下标随机访问。
- 插入删除不多(尤其不是在中间插)。
2. std::list —— “珍珠项链”
- 特点:每颗珠子(节点)用线(指针)串起来,插拔容易。
- 查找:得一颗颗摸——
O(n)
,而且分散指针不利于 CPU 缓存。
#include <list> #include <algorithm> #include <iostream> int main() { std::list<int> lst{1, 2, 3, 4, 5}; // 一串节点 auto it = std::find(lst.begin(), lst.end(), 3); // ① 线性查找 std::cout << (it != lst.end() ? "找到了" : "没找到") << '\n'; }
什么时候用插入 / 删除动作编程客栈特别频繁、但不太在意查找速度,比如任务队列需要随时插入或移走元素。
3. std::set —— 自动排序的“有序字典”
- 底层:红黑树,像一个能自动平衡的家谱图。
- 查找 / 增删:都 O(log n)。
#include <set> #include <iostream> int main() { std::set<int> s{4, 1, 3, 2}; // 元素自动有序:{1,2,3,4} bool has2 = s.contains(2); // C++20 起可直接 contains std::cout << (has2 ? "有 2" : "没 2") << '\n'; }
什么时候用既想要“去重”又想要“元素排好序”,而且单个元素查找要快。
4. std::unordered_set —— “散列表”小黑屋
- 底层:哈希表,把元素按哈希值分到不同“桶”里。
- 平均查找:O(1),最坏冲突多时 O(n)。
- 元素无序:放进去啥顺序不管。
#include <unordered_set> #include <iostream> int main() { std::unordered_set<std::string> words; // 小黑屋摞桶 words.insert("hello"); words.insert("world"); if (words.contains("hello")) { // 平均 O(1) std::cout << "嘿~ 找到了 hello\n"; } }
什么时候用不需要顺序,只想“秒查秒存”,典型如去重、统计次数。
5. std::map vs std::unordered_map —— 键值对“双子星”
容器 | 底层 | 查找 | 是否有序 |
---|---|---|---|
map | 红黑树 | O(log n) | ✅ |
unordered_map | 哈希表 | O(1) 平均 | ❌ |
#include <map> #include <unordered_map> #include <iostream> int main() { std::map<int, std::string> ordered; // 有序 ordered[2] = "B"; ordered[1] = "A"; std::unorde编程red_map<int, std::string> hash; // 无序 hash[2] = "B"; hash[1] = "A"; std::cout << ordered[1] << ' ' << hash[1] << '\n'; }
什么时候用
- 需要按键遍历且保持有序:
map
。 - 只关心查、增、删速度:
unordered_map
。
6. std::deque —— 可以从两头装菜的“长盘子”
- 特点:首尾插入 / 删除都是 O(1);中间操作比 vector 慢点。
- 查找:线性 O(n);随机访问语法与 vector 一样。
#include <deque> #include <algorithm> #include <iostream> int main() { std::deque<int> q{1, 2, 3}; q.push_front(0); // 头部 O(1) q.push_back(4); // 尾部 O(1) bool has3 = std::find(q.begin(), q.end(), 3) != q.end(); std::cout << (has3 ? "有 3" : "没 3") << '\n'; }
什么时候用既想要随机访问,又要在首尾频繁加减元素,比如滑动窗口算法。
7. “多索引”技巧 —— 一份数据,多把钥匙
如果你既想按 ID 查,又想按 名字 查,可以:
#include <vector> #include <unordered_map> struct User { int id; std::string name; }; std::androidvector<User> users; // 主数组 std::unordered_map<int, User*> id_index; // id → 指针 std::unordered_map<std::string, User*> name_index; // name → 指针
- 插入一个用户时,三处都要更新。
- 查找时,从对应索引直接拿到指针,速度飞快。
8. 记忆卡片(纯中文朗朗上口)
小而随机选 vector
插删中间选 list要有序就 set/map求极致快 unordered_*两头忙活用 deque多条件查 自建索引最后一句话
先写代码,测一次性能——复杂度只给方向,真快还是得跑数据。祝你写得顺、跑得飞!
到此这篇关于浅谈C++ 容器查找效率的文章就介绍到这了,更多相关C++ 容器查找效率内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论