C++ Coding Interview STL Checklist

C++ Coding Interview STL Checklist

C++ is fast and expressive in a coding interview, provided you know the STL well enough to use it without stopping to think. The language also has more ways to fail quietly than most: a heap that points the wrong way, a map lookup that inserts a key, a loop bound that underflows, or an iterator that becomes invalid halfway through an erase.

This C++ coding interview STL checklist covers which containers to reach for, the traps that show up most in live rounds, and short snippets to review the night before. It assumes C++17. Ask which standard your interview environment compiles with if you plan to use C++20 features such as contains.

Pick the container in one sentence

NeedContainerSay this
Dynamic arrayvector<T>"Contiguous, O(1) amortized push_back"
Hash lookup / countingunordered_map<K,V>, unordered_set<T>"Average O(1), no order"
Ordered keys, floor/ceilingmap<K,V>, set<T>"Balanced tree, O(log n), sorted iteration"
Duplicates with ordermultiset<T>"Sorted, allows repeats"
Min/max repeatedlypriority_queue<T>"Binary heap, O(log n) push/pop"
Queue / BFSqueue<T> or deque<T>"O(1) at both ends with deque"
Stackstack<T> or vector<T>"LIFO"

Naming the complexity when you pick a container is an easy way to show you know what you're doing.

Trap 1: priority_queue is a max-heap by default

priority_queue<int> maxHeap;                               // top() is largest
priority_queue<int, vector<int>, greater<int>> minHeap;    // top() is smallest

For pairs or custom ordering, write the comparator so that it returns true when a should come after b (lower priority):

// Min-heap by distance: smallest first.
auto cmp = [](const pair<int,int>& a, const pair<int,int>& b) {
    return a.first > b.first;
};
priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(cmp)> pq(cmp);

A simpler trick: push {-dist, node} into a default max-heap. Just say out loud that you're negating.

Trap 2: operator[] inserts

unordered_map<string,int> cnt;
if (cnt["x"] > 0) { /* ... */ }   // inserts "x" with value 0!

Use count, find, or C++20 contains to check membership:

if (cnt.count("x")) { /* exists */ }
auto it = cnt.find("x");
if (it != cnt.end()) { int v = it->second; }

Inserting during iteration can also trigger a rehash and invalidate your iterators. Counting with cnt[x]++ is fine, and in fact idiomatic, because you want the key inserted.

Trap 3: size() is unsigned

vector<int> v;               // empty
for (int i = 0; i < v.size() - 1; ++i) { /* ... */ }
// v.size() - 1 wraps to a huge number: undefined behavior on v[i]

Fixes: cast first, (int)v.size() - 1, or rewrite the condition as i + 1 < v.size(). Many people store int n = v.size(); at the top of the function and use n everywhere.

Trap 4: erasing while iterating

for (auto it = m.begin(); it != m.end(); ) {
    if (it->second == 0) it = m.erase(it);   // erase returns next iterator
    else ++it;
}

For vectors, use the erase-remove idiom rather than erasing in a loop:

v.erase(remove(v.begin(), v.end(), 0), v.end());

push_back on a vector can reallocate, which invalidates every pointer, reference, and iterator into it. Don't hold &v[0] across a push.

Trap 5: comparators must be strict

sort needs a strict weak ordering. Use <, never <=:

sort(intervals.begin(), intervals.end(),
     [](const vector<int>& a, const vector<int>& b) {
         return a[0] < b[0];       // not <=
     });

A <= comparator is undefined behavior and can crash or produce garbage on inputs with duplicates.

Trap 6: multiset::erase(value) removes all copies

multiset<int> ms = {3, 3, 5};
ms.erase(3);                 // removes BOTH 3s
ms.erase(ms.find(3));        // removes one (check find != end first)

This matters in sliding-window median and "remove one occurrence" problems.

Trap 7: integer overflow

Binary search helpers

// First index with v[i] >= x
auto it = lower_bound(v.begin(), v.end(), x);
int idx = it - v.begin();         // == v.size() if none

// First index with v[i] > x
auto jt = upper_bound(v.begin(), v.end(), x);

On set and map, use the member functions s.lower_bound(x), which are O(log n). The free function lower_bound on a set iterator is linear because the iterators aren't random-access.

Strings

Handy idioms

auto [lo, hi] = minmax_element(v.begin(), v.end());   // iterators
sort(v.rbegin(), v.rend());                             // descending
vector<vector<int>> grid(m, vector<int>(n, 0));          // 2D init
for (auto& [key, val] : cnt) { /* C++17 structured bindings */ }
iota(idx.begin(), idx.end(), 0);                        // 0..n-1

Talk tracks for C++ choices

"I'll use unordered_map for average O(1) lookups. If we needed sorted keys or floor queries, I'd switch to map at O(log n)."

"This is a min-heap, so I'm passing greater<int>. The default would give me the largest first."

"I'm storing n as an int to avoid unsigned underflow in the loop bound."

Soft practice note

STL fluency comes from typing these patterns under a timer and explaining your choices as you go. If you want structured, out-loud practice sessions to drill C++ habits until they're automatic, Luna Interview is built for calm interview rehearsal and prep, ahead of the real round.

Night-before checklist

C++ rewards precision. Learn these traps once, and you'll stop losing interview minutes to them.