AlgoInsight

Free lesson · DSA Mastery - Interview Prep, End to End

C++ for DSA

Easy7 min readdsaprogramming-fundamentals

C++ for DSA

C++ is the language of choice for engineers who care about raw performance - competitive programmers, systems engineers, and candidates targeting roles where execution speed matters. Most FAANG interviewers accept it without question. It is also the language where the Standard Template Library (STL) gives you the most fine-grained control over data structures and algorithms.

This topic covers the C++ APIs and idioms that matter for DSA problems. It assumes you know C++ syntax basics. The focus is on the STL containers and the patterns that show up in interview code.

Why C++ for Interviews

Interactive animation: What C++ gives you in an interview, and what it costs.

C++ has specific advantages:

The trade-offs:

The Core STL Containers

Interactive animation: Every container you need, built in - with the key calls and costs.

The STL has every container you need for interview problems.

Need C++ Type Notes
Dynamic array vector<T> The default array-like container
Hash map unordered_map<K, V> O(1) average
Sorted map map<K, V> O(log n), red-black tree
Hash set unordered_set<T> O(1) average
Sorted set set<T> O(log n), red-black tree
Stack stack<T> LIFO
Queue queue<T> FIFO
Deque deque<T> Double-ended, used for sliding window
Priority queue priority_queue<T> Max-heap by default
Linked list list<T> Doubly linked, rarely needed

The non-unordered_ versions (map, set) are tree-based and keep their elements sorted. The unordered_ versions are hash-based and faster on average but unordered.

Interactive animation: Hash-based or tree-based: unordered_map against map, unordered_set against set.

Vectors

Interactive animation: vector line by line: building, reading, growing and passing it around.

vector<T> is C++'s dynamic array. It is the default container for most interview problems.

#include <vector>

vector<int> v = {5, 2, 8, 1, 9, 3};
v.push_back(10);          // add to end
v.pop_back();             // remove from end
v[0];                     // index, no bounds check
v.at(0);                  // index, bounds-checked (throws)
v.front();                // first element
v.back();                 // last element
v.size();                 // size
v.empty();                // is empty?
v.clear();                // remove all elements

To iterate:

for (int i = 0; i < v.size(); i++) {
    cout << v[i];
}

for (int x : v) {
    cout << x;
}

for (auto it = v.begin(); it != v.end(); ++it) {
    cout << *it;
}

The range-based for loop (for (int x : v)) is the cleanest. Use it when you do not need the index.

For 2D vectors:

vector<vector<int>> grid(3, vector<int>(4, 0));
// 3x4 grid initialized to 0

grid[1][2] = 5;

Unordered Map

Interactive animation: Count, check and walk a hash map - and why count() is safer than [] for reading.

unordered_map<K, V> is C++'s hash map.

#include <unordered_map>

unordered_map<string, int> counts;
counts["a"]++;             // creates entry if missing, initializes to 0
counts["a"] = 5;
counts["b"] = 10;

if (counts.count("a")) {
    int value = counts["a"];
}

// Or use find:
auto it = counts.find("a");
if (it != counts.end()) {
    int value = it->second;
}

One useful behavior: counts["a"]++ works even when "a" is not in the map. The bracket operator auto-creates missing entries with the value-type's default (0 for int). This is Python-like and very useful for counting patterns.

To iterate:

for (auto& [key, value] : counts) {
    cout << key << ": " << value;
}

The structured binding auto& [key, value] requires C++17. Older code uses it->first and it->second.

Unordered Set

Similar to unordered_map but stores only keys.

#include <unordered_set>

unordered_set<int> seen;
seen.insert(5);
if (seen.count(5)) {
    // present
}
seen.erase(5);

For sorted versions, use set<T> and map<K, V> (from <set> and <map> headers). They have the same API but with O(log n) operations and keep elements sorted.

Stack and Queue

Interactive animation: stack, queue and deque - and the surprise that pop() returns nothing.

stack<T> and queue<T> are container adapters built on top of deque<T>.

#include <stack>
#include <queue>

stack<int> s;
s.push(1);
s.push(2);
s.top();              // 2 (does NOT pop)
s.pop();              // removes top but does NOT return it
s.empty();

queue<int> q;
q.push(1);
q.push(2);
q.front();            // 1
q.back();             // 2
q.pop();              // removes front

The notable thing: pop() does not return the element. To both peek and remove, do s.top() then s.pop(). This trips up engineers coming from Java and Python.

Deque

deque<T> is a double-ended queue with O(1) operations at both ends. Use it for sliding window and monotonic queue problems.

#include <deque>

deque<int> dq;
dq.push_front(1);
dq.push_back(2);
dq.pop_front();
dq.pop_back();
dq.front();
dq.back();

deque also supports random-access indexing like a vector, which queue and stack do not.

Priority Queue

Interactive animation: C++'s priority_queue is a MAX-heap by default; greater<int> makes it a min-heap.

priority_queue<T> is C++'s heap. By default, it is a max-heap - opposite to Python and Java.

#include <queue>

priority_queue<int> maxHeap;
maxHeap.push(5);
maxHeap.push(2);
maxHeap.push(8);
maxHeap.top();        // 8
maxHeap.pop();        // remove top

For a min-heap, you have to be more verbose:

priority_queue<int, vector<int>, greater<int>> minHeap;
minHeap.push(5);
minHeap.push(2);
minHeap.push(8);
minHeap.top();        // 2

The template parameters are: element type, underlying container, comparator. greater<int> reverses the ordering.

For custom comparators with structs or pairs:

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

This is awkward but unavoidable. For interview code, a lambda passed to the constructor is the cleanest pattern.

Strings

C++ strings are mutable and have a rich API.

#include <string>

string s = "hello";
s.length();           // 5
s.size();             // 5 (same as length)
s[0];                 // 'h'
s.push_back('!');     // "hello!"
s.pop_back();         // "hello"
s.substr(1, 3);       // "ell" (start, length)
s.find("ll");         // 2 (npos if not found)
s.append(" world");   // "hello world"
s += " world";        // same
s.compare("hello");   // 0 if equal
reverse(s.begin(), s.end());

For conversions:

int n = stoi("42");
double d = stod("3.14");
string s = to_string(42);

For character checks (from <cctype>):

isdigit('5');         // true
isalpha('a');         // true
isalnum('a');         // true
tolower('A');         // 'a'
toupper('a');         // 'A'

Sorting

Interactive animation: sort with greater, with a lambda, and on intervals.

sort is in <algorithm> and uses Introsort (a hybrid of quicksort, heapsort, and insertion sort).

#include <algorithm>

vector<int> v = {3, 1, 4, 1, 5};
sort(v.begin(), v.end());                    // ascending
sort(v.begin(), v.end(), greater<int>());    // descending
sort(v.begin(), v.end(), [](int a, int b) {
    return abs(a) < abs(b);                  // custom comparator
});

For 2D vectors:

vector<vector<int>> intervals = {{1, 3}, {2, 6}, {8, 10}};
sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b) {
    return a[0] < b[0];
});

Note that sort takes iterators, not the container directly. This is true throughout <algorithm>.

Pairs and Tuples

Interactive animation: pair and tuple: bundling values without writing a class, and a heap of pairs.

pair<A, B> and tuple<A, B, C, ...> are useful when you need to bundle a couple of values without defining a class.

#include <utility>
#include <tuple>

pair<int, string> p = {42, "answer"};
p.first;
p.second;

auto [n, s] = p;      // structured binding (C++17)

tuple<int, double, string> t = {1, 2.5, "hello"};
get<0>(t);            // 1
get<1>(t);            // 2.5
auto [a, b, c] = t;   // structured binding

Pairs are widely used in graph problems where you need (node, distance) or in heap problems where you need (priority, value).

Common Idioms

A few patterns that recur in C++ interview code.

Counting with unordered_map

unordered_map<char, int> counts;
for (char c : s) {
    counts[c]++;
}

Iterating an unordered_map

for (auto& [key, value] : counts) {
    // ...
}

Sum with accumulate

#include <numeric>

int total = accumulate(v.begin(), v.end(), 0);

Max and min over a range

int maxVal = *max_element(v.begin(), v.end());
int minVal = *min_element(v.begin(), v.end());

Binary search on a sorted range

bool found = binary_search(v.begin(), v.end(), target);
auto it = lower_bound(v.begin(), v.end(), target);  // first not less than
auto it2 = upper_bound(v.begin(), v.end(), target); // first greater than

lower_bound and upper_bound are essential for problems involving sorted arrays. They are O(log n) and return iterators.

Tree Node and List Node Classes

Interactive animation: The TreeNode and ListNode structs interview problems are built on.

Standard interview classes look like:

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

Most C++ interviews use raw pointers for these. Modern C++ would use unique_ptr or shared_ptr, but interview code rarely bothers.

Performance Tips for Interviews

Interactive animation: Three performance tips for interview C++.

A few things specific to competitive-style C++ code.

Disable iostream synchronization for fast I/O:

ios_base::sync_with_stdio(false);
cin.tie(NULL);

This is for problems with large I/O. Most interview problems do not need it, but if you are facing a strict time limit, it can help.

Reserve vector capacity when the size is known:

vector<int> v;
v.reserve(n);
for (int i = 0; i < n; i++) v.push_back(i);

Avoids the amortized cost of reallocation.

Use emplace_back instead of push_back for non-trivial types:

vector<pair<int, int>> v;
v.emplace_back(1, 2);    // constructs in place
v.push_back({1, 2});     // constructs then moves (one extra step)

For interview code, push_back is usually fine.

Common Mistakes to Avoid

Interactive animation: Two mistakes that compile and still give wrong answers: overflow and size() - 1.

Integer overflow. int is 32-bit. Computations involving products of integers up to 10^5 can overflow. Use long long when in doubt:

long long product = (long long)a * b;

Signed/unsigned comparison. v.size() returns size_t (unsigned). Comparing i < v.size() - 1 when v.size() is 0 wraps around to a huge value. Cast to int or use i + 1 < v.size() instead.

Reference vs copy. for (auto x : v) copies each element. For non-trivial types, use for (auto& x : v) to avoid the copy.

Iterator invalidation. Modifying a container while iterating over it can invalidate iterators. Either iterate by index, or collect changes and apply them after.

auto deduction. auto x = vec[0] gives you a copy, not a reference. Use auto& x if you want a reference.

Boilerplate to Remember

Interactive animation: An interview template to start every C++ solution from.

A minimal C++ interview template:

#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
    int solve(vector<int>& nums) {
        unordered_map<int, int> counts;
        for (int x : nums) counts[x]++;
        return 0;
    }
};

The <bits/stdc++.h> include is GCC-specific and pulls in the entire standard library. Most interview platforms support it. It saves you from remembering individual headers.

using namespace std; is controversial in production code but standard in interview code - it keeps the syntax readable.

The rest of this course uses Java for code examples. If C++ is your interview language, the algorithmic ideas transfer directly. Read the Java code as pseudocode and translate using the patterns covered here.

Back to DSA Mastery - Interview Prep, End to End