STL Containers
vector, list, deque, map, set
1. The Container Family
| Container | Order | Lookup | Insert |
|---|---|---|---|
vector | Insertion (contiguous) | O(1) by index | O(1) amortized at end |
deque | Insertion | O(1) by index | O(1) at both ends |
list | Insertion (linked) | O(n) | O(1) anywhere with iter |
set | Sorted | O(log n) | O(log n) |
map | Sorted by key | O(log n) | O(log n) |
unordered_set | Hashed | O(1) avg | O(1) avg |
unordered_map | Hashed by key | O(1) avg | O(1) avg |
2. std::vector — The Default
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9};
v.push_back(2);
v.insert(v.begin() + 2, 99);
v.erase(v.begin());
for (int x : v) std::cout << x << " ";
std::cout << "\n";
}
3. std::map & std::unordered_map
#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
int main() {
std::map<std::string, int> ages; // sorted, O(log n)
ages["sara"] = 21;
ages["karim"] = 25;
ages["ali"] = 30;
for (const auto& [name, age] : ages)
std::cout << name << " -> " << age << "\n";
std::unordered_map<std::string, int> um; // hashed, O(1) avg
um["x"] = 1;
if (auto it = um.find("x"); it != um.end())
std::cout << it->second << "\n";
}
4. std::set — Unique, Sorted
#include <iostream>
#include <set>
int main() {
std::set<int> s = {3, 1, 4, 1, 5}; // dupes removed
s.insert(2);
for (int x : s) std::cout << x << " "; // 1 2 3 4 5
}
5. Container Adaptors
Built on top of containers, restricting the interface:
std::stack<T>— LIFO (default backing: deque)std::queue<T>— FIFO (default: deque)std::priority_queue<T>— heap-based, max at top
6. Choosing the Right Container
- Need key→value lookup? →
unordered_map(ormapif order matters) - Need uniqueness? →
unordered_set/set - Need fast insert at both ends? →
deque - Otherwise →
vector
Almost never use std::list — its constant overhead and cache misses outweigh the O(1) middle-insert in practice.
7. Practice Problems
- Count word frequencies with map.
✨ Show Answer
std::map<std::string, int> freq; for (const auto& w : words) ++freq[w]; - Why prefer unordered_map for huge data?
✨ Show Answer
O(1) average lookup vs map's O(log n). For 1M entries, that's 1 step vs 20. Use ordered map only when iteration order matters.
- Find unique elements in a vector.
✨ Show Answer
std::set<int> uniq(v.begin(), v.end()); - Push to a stack and pop.
✨ Show Answer
std::stack<int> s; s.push(1); s.push(2); std::cout << s.top(); s.pop(); - Why is iteration on map slower than on vector?
✨ Show Answer
Map is a tree with scattered nodes — cache misses every step. Vector is contiguous — prefetcher loves it.
- Sort a vector ascending.
✨ Show Answer
std::sort(v.begin(), v.end()); - Sort descending.
✨ Show Answer
std::sort(v.begin(), v.end(), std::greater<>{}); - Iterator invalidation: when does push_back invalidate iterators?
✨ Show Answer
If reallocation happens (size exceeds capacity), all iterators are invalidated. Reserve enough up front to avoid this.
- Build a priority_queue of pairs (priority, item).
✨ Show Answer
std::priority_queue<std::pair<int, std::string>> pq; pq.push({5, "high"}); pq.push({1, "low"}); std::cout << pq.top().second; // high - Difference between map[k] and map.find(k)?
✨ Show Answer
m[k]inserts a default value if key missing.m.find(k)returns an iterator (orend()) without modifying the map. - Min-heap from priority_queue.
✨ Show Answer
std::priority_queue<int, std::vector<int>, std::greater<>> minHeap; - Reserve capacity 1M before push_back loop. Why?
✨ Show Answer
Avoids many reallocations and copies as size grows. Big win for large vectors.
- Erase even numbers from a vector.
✨ Show Answer
v.erase(std::remove_if(v.begin(), v.end(), [](int x){ return x % 2 == 0; }), v.end());(C++20:
std::erase_if(v, ...).) - Convert vector to set in one line.
✨ Show Answer
std::set<int> s(v.begin(), v.end()); - Why almost never use std::list?
✨ Show Answer
Cache-unfriendly. Even with O(1) middle insert, a vector with shifting is faster on small/medium data due to memory locality.
- Iterate map by keys only.
✨ Show Answer
for (const auto& [k, _] : m) std::cout << k; - Insert only if absent.
✨ Show Answer
auto [it, inserted] = m.insert({"a", 1}); - Sum of all map values.
✨ Show Answer
int total = 0; for (const auto& [k, v] : m) total += v; - Group elements by parity in two vectors.
✨ Show Answer
std::vector<int> even, odd; for (int x : v) (x % 2 ? odd : even).push_back(x); - Find median of vector.
✨ Show Answer
std::nth_element(v.begin(), v.begin() + v.size()/2, v.end()); int median = v[v.size()/2]; - unordered_map of int → vector<int> (adjacency list).
✨ Show Answer
std::unordered_map<int, std::vector<int>> graph; graph[1].push_back(2); graph[1].push_back(3); - When does std::map's iterator stay valid?
✨ Show Answer
Always, except for iterators to erased elements. Tree structure means insert/erase don't shift the rest. (Vector is the opposite.)
Summary
std::vector is the default. std::unordered_map for fast lookup;
std::map when iteration order matters. std::set for uniqueness. Container adaptors
(stack, queue, priority_queue) wrap a sequence container. Iterator invalidation is the #1 STL bug — be aware
of which operations invalidate which iterators.