Yilong Wu

我是 AlanWu。刚开始学习 C++ 时,我对“我需要存储多个东西”这个问题的回答总是数组。int arr[1000]。每一次都是如此。

后来我发现了 STL 容器,才意识到自己之前太辛苦了。下面是何时该用什么。


vector —— 你的新默认选择

如果你过去会写 int arr[1000],那么现在请改用 vector<int> arr。它可以增长、可以缩小,还知道自己的大小,而且你把它传给函数时不必再单独传长度。

// Old way
int scores[100];
int n = 0;
scores[n++] = 95;  // hope n doesn't exceed 99

// New way
vector<int> scores;
scores.push_back(95);  // never overflow, never count manually

Enter fullscreen mode Exit fullscreen mode

使用 vector 的场景:你需要一组有序的元素,主要在末尾追加或通过索引访问。

不使用 vector 的场景:你需要频繁在中间插入或删除元素。那是 O(n) 操作——后续所有元素都需要移动。


map —— 当你需要按名称查找时

数组和 vector 使用整数索引。如果你想通过姓名而不是数字来查询学生的分数呢?

map<string, int> scores;
scores["Alice"] = 95;
scores["Bob"] = 88;

cout << scores["Alice"];  // 95

Enter fullscreen mode Exit fullscreen mode

map 的底层是一个平衡二叉搜索树。查找复杂度为 O(log n),而不是 O(1)。但对于大多数场景,它已经足够快,而且代码极其简洁。

使用 map 的场景:你有键值对,且键不只是 0、1、2、3……

不使用 map 的场景:你只需要整数索引——那正是 vector 的用武之地。或者当你需要平均 O(1) 的查找时——这时应该用 unordered_map


unordered_map —— 同样的功能,但通常更快

unordered_map<string, int> scores;
scores["Alice"] = 95;

Enter fullscreen mode Exit fullscreen mode

它看起来和 map 完全一样。区别在于 unordered_map 使用哈希表。平均查找复杂度为 O(1) 而非 O(log n)。但最坏情况下(发生哈希冲突时)会退化为 O(n),键没有顺序,且占用更多内存。

我的原则是:先用 unordered_map。如果需要键有序,再切换到 map。


set —— 当你只关心某物是否存在时

需要记录你已经见过的 ID?不要用 vector 每次都遍历一遍。

set<int> seen;
seen.insert(42);
seen.insert(17);

if (seen.count(42)) {  // true — O(log n)
    // already seen it
}

Enter fullscreen mode Exit fullscreen mode

set 存储唯一值并保持有序。不允许重复。

如果需要平均 O(1) 且不需要有序:使用 unordered_set


实际的决策树

  • 你是否通过位置(0、1、2…)访问?→ vector。
  • 你是否需要通过非数字的键查找?→ unordered_map。
  • 这些键是否需要保持有序?→ map。
  • 你是否只关心“我是否见过这个”?→ unordered_set。
  • 你是否需要这些值有序或不允许重复?→ set。

这覆盖了 90% 的场景。


你大概不需要的容器:deque

我看到初学者因为 deque 听起来很酷而使用它。双端队列,可以在两端插入。

除非你正在写滑动窗口算法或调度器,否则你不需要 deque。vector 配合 push_back 更快也更简单。


总结

下次你想要用普通数组时,先问问自己:vector、map 或 set 能否用一半的代码完成这件事?答案通常是肯定的。

我的 GitHub:https://github.com/Cn-Alanwu