⚡ Practical Interview Reference

C++ STL Containers & Functions

A practical reference for the exact confusion most learners hit: insert vs push_back vs emplace, how to delete, access, replace, search, iterate, and which functions exist for which STL data structure.

vectordequelist setmultisetunordered_set mapunordered_map stackqueuepriority_queue arraystring
Sequence vector / deque / list
Unique keys set / unordered_set
Key → value map / unordered_map
Restricted API stack / queue / priority_queue
● Add / Insert ● Delete / Remove ● Access / Update ● Search / Lookup

The mental model ↑ top

Do not memorize random functions. First identify what kind of container you are dealing with.

1. Sequence containers

vector, deque, list

Elements have a sequence/order. You usually add elements with push_back, push_front where supported, or insert.

2. Set-like containers

set, multiset, unordered_set

No “back” position. Therefore no push_back(). Use insert() or emplace().

3. Key-value containers

map, unordered_map

Store key → value. Use m[key], insert, emplace, insert_or_assign, etc.

4. Container adapters

stack, queue, priority_queue

Restricted interfaces. You cannot freely index or iterate them like a vector.

Your specific doubt: yes, set absolutely has insert(). Example: set<int> s; s.insert(10);.

Master function table ↑ top

ContainerAdd at backAdd at frontGeneral insert DeleteAccessSearch
vectorpush_back / emplace_backNo push_frontinsert / emplacepop_back / erase[], at, front, backstd::find
dequepush_backpush_frontinsert / emplacepop_back / pop_front / erase[], at, front, backstd::find
listpush_backpush_frontinsert / emplacepop_* / erase / removefront, backstd::find
setNoNoinsert / emplaceeraseNo indexfind / contains
multisetNoNoinsert / emplaceeraseNo indexfind / contains / count
unordered_setNoNoinsert / emplaceeraseNo indexfind / contains
mapNoNoinsert / emplaceerasem[key], atfind / contains
unordered_mapNoNoinsert / emplaceerasem[key], atfind / contains
stackpush / emplaceNo arbitrary insertpoptopNo find
queuepush / emplaceNo arbitrary insertpopfront, backNo find
priority_queuepush / emplaceNo arbitrary insertpoptopNo find

Note: contains() is C++20. Before C++20, use find(x) != container.end().

vector<data_type> arr — full practical reference ↑ top

#include <vector>
using namespace std;

vector<int> arr;
vector<int> a = {10, 20, 30};
vector<int> b(5);        // 5 integers, initialized to 0
vector<int> c(5, 7);     // {7, 7, 7, 7, 7}
TaskSyntaxMeaning
Add at endarr.push_back(x);Append a copy/move of x
Construct at endarr.emplace_back(args...);Construct element directly at end
Insert before positionarr.insert(arr.begin()+i, x);Insert x before index i
Construct before positionarr.emplace(arr.begin()+i, args...);Construct directly at that position
Delete lastarr.pop_back();Removes final element; returns nothing
Delete indexarr.erase(arr.begin()+i);Deletes one element
Delete rangearr.erase(arr.begin()+l, arr.begin()+r);Deletes indices [l, r)
Replace/updatearr[i] = newValue;Replace element at index i
Read by indexarr[i]Fast; no bounds checking
Checked readarr.at(i)Throws if index is invalid
Firstarr.front()First element
Lastarr.back()Last element
Number of elementsarr.size()Returns size
Empty?arr.empty()Boolean
Remove everythingarr.clear()Size becomes 0
Reserve memoryarr.reserve(n)Capacity at least n
Resizearr.resize(n)Changes number of elements
Capacityarr.capacity()Allocated storage before reallocation
Iteratorsbegin(), end(), rbegin(), rend()Used with algorithms and loops

Examples

vector<int> arr = {10, 20, 30};

arr.push_back(40);                // {10,20,30,40}
arr.insert(arr.begin() + 1, 15);  // {10,15,20,30,40}

arr[2] = 99;                      // {10,15,99,30,40}

arr.erase(arr.begin() + 3);       // {10,15,99,40}
arr.pop_back();                   // {10,15,99}

cout << arr.front();             // 10
cout << arr.back();              // 99
cout << arr.size();              // 3
Important: a vector does not have push_front(). You can insert at begin(), but that is usually O(n) because existing elements must shift.

deque ↑ top

Think: “vector-like indexing, but efficient insertion/removal at both ends.”

deque<int> dq = {20, 30};

dq.push_front(10);     // {10,20,30}
dq.push_back(40);      // {10,20,30,40}

dq.pop_front();        // {20,30,40}
dq.pop_back();         // {20,30}

dq[0] = 99;
dq.insert(dq.begin()+1, 50);
dq.erase(dq.begin());

Useful functions: push_front, push_back, emplace_front, emplace_back, insert, emplace, pop_front, pop_back, erase, [], at, front, back.

list ↑ top

std::list is a doubly linked list. It supports fast insertion/removal when you already have an iterator, but no random indexing.

list<int> li = {10, 20, 30};

li.push_front(5);
li.push_back(40);

li.pop_front();
li.pop_back();

auto it = li.begin();
++it;
li.insert(it, 15);
li.erase(it);

li.remove(20);      // remove every element equal to 20
li.reverse();
li.sort();
li[2] is invalid. A list has no operator[].

set and multiset ↑ top

A set stores unique sorted keys. A multiset is sorted but allows duplicates.

set<int> s;

s.insert(10);
s.insert(30);
s.insert(20);
s.insert(10);        // duplicate: not added

// s = {10, 20, 30}
Taskset syntax
Inserts.insert(x);
Construct in sets.emplace(args...);
Finds.find(x)
Check existences.contains(x) (C++20)
Counts.count(x) — for set, result is 0 or 1
Delete by values.erase(x);
Delete by iterators.erase(it);
First value ≥ xs.lower_bound(x)
First value > xs.upper_bound(x)
Clears.clear()

Checking if something exists

if (s.find(20) != s.end()) {
    cout << "Found";
}

// C++20:
if (s.contains(20)) {
    cout << "Found";
}

Why no push_back()?

A set decides the storage position automatically according to ordering. There is no meaningful “back insertion” operation.

multiset deletion trap

multiset<int> ms = {2, 2, 2, 5};

ms.erase(2);             // removes ALL 2s

auto it = ms.find(2);
if (it != ms.end())
    ms.erase(it);        // removes only ONE 2

unordered_set ↑ top

Same basic set operations, but hash-based and not sorted.

unordered_set<string> seen;

seen.insert("evt1");
seen.emplace("evt2");

if (seen.find("evt1") != seen.end()) {
    cout << "Already seen";
}

seen.erase("evt1");

Average complexity: insertion/search/deletion ≈ O(1). Worst case can be O(n).

map / unordered_map ↑ top

A map stores key → value pairs.

unordered_map<string, int> freq;

freq["apple"] = 3;     // insert or replace
freq["apple"]++;       // increment
freq["banana"] = 1;
TaskSyntaxImportant detail
Insert/accessm[key]If missing, it creates a default value
Checked accessm.at(key)Does not create missing key; throws if absent
Insert pairm.insert({key, value});Does not overwrite existing key
Emplace pairm.emplace(key, value);Constructs pair in container
Insert if absentm.try_emplace(key, value);Useful for expensive mapped objects
Insert or overwritem.insert_or_assign(key, value);Exactly what the name says
Findm.find(key)Returns iterator or end()
Existencem.contains(key)C++20
Deletem.erase(key)Erase by key

Iterating

for (auto &entry : freq) {
    cout << entry.first << " " << entry.second << '\n';
}

// Cleaner with structured bindings:
for (auto &[key, value] : freq) {
    cout << key << " " << value << '\n';
}
map is sorted by key and usually gives O(log n) search/insert/erase. unordered_map is hash-based and averages O(1).

stack, queue, priority_queue ↑ top

stack — LIFO

stack<int> st;

st.push(10);
st.push(20);

cout << st.top();  // 20
st.pop();           // removes 20

st.empty();
st.size();

No back(), no indexing, no erase(), no direct iteration.

queue — FIFO

queue<int> q;

q.push(10);
q.push(20);

cout << q.front(); // 10
cout << q.back();  // 20

q.pop();             // removes 10
q.empty();
q.size();

priority_queue

priority_queue<int> pq;

pq.push(10);
pq.push(50);
pq.push(20);

cout << pq.top();   // 50
pq.pop();

Default is max-heap.

priority_queue<int,
    vector<int>,
    greater<int>> minHeap;
For adapters, the insertion function is simply push() or emplace(), not push_back().

array and string ↑ top

std::array

array<int, 4> a = {10,20,30,40};

a[1] = 99;
cout << a.at(2);
cout << a.front();
cout << a.back();
cout << a.size();

a.fill(7);

std::array has fixed size, so no push_back, insert, erase, or resize.

std::string

string s = "hello";

s.push_back('!');          // "hello!"
s.pop_back();              // "hello"
s += " world";             // append
s.append("!");             // append
s.insert(5, " nice");      // insert
s.erase(5, 5);             // erase range by index/count
s.replace(0, 5, "Hi");     // replace substring

char c = s[0];
size_t pos = s.find("world");

insert vs emplace vs push_back ↑ top

FunctionWhat it conceptually doesTypical containers
push_back(x)Add already-created value x at endvector, deque, list
emplace_back(args...)Construct new object directly at end from constructor argumentsvector, deque, list
insert(...)Insert a value/pair into a specified position or associative containerMost containers except adapters
emplace(...)Construct an element directly inside the containerMost standard containers
push(x)Add an element through an adapter interfacestack, queue, priority_queue

Simple types: do not overthink emplace

vector<int> v;
v.push_back(10);       // perfectly fine
v.emplace_back(10);    // also fine, but no meaningful advantage here

Objects: emplace_back becomes more natural

struct Student {
    string name;
    int age;

    Student(string n, int a) : name(n), age(a) {}
};

vector<Student> students;

students.push_back(Student("Mohammed", 21));
students.emplace_back("Mohammed", 21);
For coding interviews, a safe rule is: use push_back() for vectors unless constructing an object in-place is useful. For set/map, both insert and emplace are common.

Deletion / erase patterns ↑ top

Vector: erase by index

v.erase(v.begin() + i);

Vector: erase a range

v.erase(v.begin() + l, v.begin() + r); // [l, r)

Set/map: erase by value/key

s.erase(x);
m.erase(key);

Erase while iterating

for (auto it = v.begin(); it != v.end(); ) {
    if (*it < 0)
        it = v.erase(it);
    else
        ++it;
}

Remove all occurrences from vector

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

In C++20 you can also use:

erase(v, x);
erase_if(v, [](int x) { return x < 0; });
pop_back(), pop(), and pop_front() return void. Read the element first if you need it.

How to replace / update elements ↑ top

ContainerHow to replace/update
vector / dequev[i] = newValue;
list*it = newValue;
map / unordered_mapm[key] = newValue; or insert_or_assign
set / unordered_setCannot directly modify stored keys. Erase old key, then insert new key.
stack / queue / priority_queueNo arbitrary replacement; usually pop then push according to required logic.
// set replacement
set<int> s = {10,20,30};

s.erase(20);
s.insert(25);

Finding elements ↑ top

Sequence containers

Use the generic algorithm std::find.

auto it = find(v.begin(), v.end(), 30);

if (it != v.end()) {
    cout << "Found";
}

Associative containers

Use the container's own find().

if (s.find(30) != s.end()) { ... }
if (m.find("alice") != m.end()) { ... }
Prefer s.find(x) over std::find(s.begin(), s.end(), x) for sets/maps, because the container-specific lookup uses the data structure efficiently.

Iterators: why begin() keeps appearing ↑ top

An iterator acts roughly like a pointer to an element.

vector<int> v = {10,20,30};

auto it = v.begin();   // points to 10

cout << *it;          // 10
++it;
cout << *it;          // 20

v.end();               // points ONE POSITION AFTER the last element

For vector random access:

v.begin() + 2   // iterator to index 2

But this is not valid for every container:

list<int> li = {10,20,30};

// li.begin() + 2;    // INVALID
auto it = li.begin();
advance(it, 2);       // works

Common mistakes to avoid ↑ top

MistakeCorrect idea
set.push_back(x)set.insert(x)
stack.push_back(x)stack.push(x)
queue.pop_front()queue.pop()
vector.length()vector.size()
arr.erase(i) for vector indexarr.erase(arr.begin()+i)
set[i]Sets are not indexable
list[i]Lists are not indexable
m.find(key) == truem.find(key) != m.end()
int x = st.pop()x = st.top(); st.pop();
Modify a key inside setErase old value, then insert new value
Use m[key] only to check existenceUse find or contains; [] may create the key
Assume unordered_* iteration is sortedHash containers have no sorted iteration order

Which function should I use? — 20-second decision guide ↑ top

I have a vector

  • Add at end → push_back(x)
  • Delete end → pop_back()
  • Insert at index → insert(begin()+i, x)
  • Delete index → erase(begin()+i)
  • Replace → v[i] = x
  • Search → std::find

I have a set

  • Add → insert(x)
  • Delete → erase(x)
  • Search → find(x) / contains(x)
  • Replace → erase old, insert new
  • No indexes, no push_back

I have a map

  • Set/update → m[key] = value
  • Increment count → m[key]++
  • Insert pair → insert / emplace
  • Delete → erase(key)
  • Search → find(key) / contains(key)

I have a stack/queue

  • Add → push(x)
  • Remove → pop()
  • Stack read → top()
  • Queue read → front()
  • No indexing or arbitrary erase

Final memory trick

vector/deque/list → push_back
deque/list        → push_front too
set/map           → insert
stack/queue/heap  → push

Need in-place construction?
push_back  → emplace_back
push       → emplace
insert     → emplace
For interviews: prioritize getting the container semantics right before chasing micro-optimizations. Using push_back instead of emplace_back for an int is not the kind of mistake that matters. Using push_back on a set is.
C++ STL Practical Reference • designed as a local revision sheet
↑