Free Handbook · Every example compiled & verified

Problem Solving

A repeatable way to crack coding problems in C++: read it, break it down, test small cases, then apply one of eight patterns that cover most interviews.

0 / 145 lessons🔥 0 day streak
ShareXLinkedIn

Module 14 · what you'll be able to do

  • Turn a problem statement into inputs, outputs, constraints and edge cases before writing any code
  • Test a solution with a tiny hand-rolled check function instead of guessing
  • Estimate the Big-O a problem needs from its input limits, in plain English
  • Recognise and apply eight patterns in C++: two pointers, sliding window, hash map lookups, stack, backtracking, sorting, BFS/DFS and DP-lite
01

Read the problem before touching the keyboard

Most failed coding rounds fail in the first two minutes: the candidate starts typing a solution to a problem they have not fully read. Slow down and write these five things down (in a comment, on the whiteboard, or out loud) before any code.

  1. 1
    Restate it

    One sentence, in your own words. "Given a list of prices, return the biggest profit from one buy followed by one later sell."

  2. 2
    Inputs and outputs, with types

    const std::vector<int>& prices in, int out. Can the vector be empty? What is returned then? Should "no answer" be a std::optional?

  3. 3
    Constraints

    How big is n? Can values be negative? Duplicates? Already sorted? These decide the algorithm (see the Big-O lesson below).

  4. 4
    Two examples by hand

    One normal, one tricky. Work them out on paper. If you cannot do it by hand, you cannot code it.

  5. 5
    Edge cases

    Empty input, one element, all equal, already sorted, and values big enough to overflow int. Signed overflow is undefined behaviour in C++, so use long long when sums can exceed about two billion (Module 01).

C++main.cpp
#include <algorithm>
#include <climits>
#include <iostream>
#include <vector>

// Restated: best profit from one buy then one later sell; 0 if prices only fall.
// Input: prices (may be empty). Output: int >= 0.
int maxProfit(const std::vector<int>& prices) {
    int lowest = INT_MAX, best = 0;
    for (int price : prices) {
        lowest = std::min(lowest, price);      // cheapest buy so far
        best = std::max(best, price - lowest); // what if I sell today?
    }
    return best;
}

int main() {
    std::cout << maxProfit({7, 1, 5, 3, 6, 4}) << '\n'; // normal: buy at 1, sell at 6
    std::cout << maxProfit({7, 6, 4, 3, 1}) << '\n';    // prices only fall
    std::cout << maxProfit({}) << '\n';                 // empty
    std::cout << maxProfit({5}) << '\n';                // one day
}
Outputcompiled & run with real C++
5
0
0
0

The two comments at the top are the reading step. The four calls are the examples and edge cases from that step, written before the function body, not bolted on after.

Your turn

Change the contract to "return std::optional<std::pair<int, int>>: the buy day and sell day, or std::nullopt if no profit is possible". Which of the four calls now needs a different expected answer?

02

Break it down and test with small cases

Break the problem into pieces you can test separately: a helper that checks one thing, a loop that applies it. Then test with the smallest inputs that could break it. In an interview, and in your own practice, a tiny check lambda is faster than setting up GoogleTest, and it shows the interviewer you verify your own code.

C++main.cpp
#include <cctype>
#include <iostream>
#include <string>

bool isPalindrome(const std::string& s) {
    int i = 0, j = static_cast<int>(s.size()) - 1;
    while (i < j) {
        unsigned char a = s[i], b = s[j];
        if (!std::isalnum(a)) { ++i; continue; }
        if (!std::isalnum(b)) { --j; continue; }
        if (std::tolower(a) != std::tolower(b)) return false;
        ++i;
        --j;
    }
    return true;
}

int main() {
    int passed = 0, failed = 0;
    auto check = [&](const std::string& input, bool expected) {
        if (isPalindrome(input) == expected) { ++passed; return; }
        ++failed;
        std::cout << "FAIL '" << input << "': expected " << std::boolalpha << expected << '\n';
    };

    check("", true);                          // empty
    check("a", true);                         // one character
    check("ab", false);                       // smallest false case
    check("Aba", true);                       // mixed case
    check("A man, a plan, a canal: Panama", true);
    check("race a car", false);
    check(".,", true);                        // only punctuation
    std::cout << passed << " passed, " << failed << " failed\n";
}
Outputcompiled & run with real C++
7 passed, 0 failed

The lambda captures passed and failed by reference ([&]). The characters are read as unsigned char because passing a negative char to std::isalnum is undefined behaviour.

Your turn

Break isPalindrome on purpose (drop the two std::tolower calls) and run it. Exactly two cases report FAIL. That is how a good small-case list pinpoints a bug.

The order to test in
Empty, one element, two elements, the example from the problem, then one case aimed at each branch of your code. Small inputs make a wrong answer obvious by eye. Compile with -fsanitize=address,undefined while practising, so an out-of-bounds index crashes loudly instead of returning garbage.
03

Big-O in plain English

Big-O answers one question: if the input gets ten times bigger, how much slower does this get? O(n) gets ten times slower. O(n²) gets a hundred times slower. O(log n) barely notices. Optimised C++ does very roughly 108 to 109 simple operations per second, which turns the constraints in a problem statement into a direct hint about which algorithm is expected.

Rules of thumb, not laws. They are good enough to rule out the wrong approach before you write it.
If n is up to…You can affordTypical approach
10 to 20O(2n) or O(n!)Backtracking: try every subset or ordering
~500O(n³)Three nested loops, small DP tables
~5,000O(n²)Two nested loops, 2-D DP
~106O(n log n)Sort first, a heap, binary search inside a loop
~108O(n)One pass: two pointers, sliding window, unordered_map
Anything largerO(log n) or O(1)Binary search on the answer, a formula
C++main.cpp
#include <iostream>
#include <numeric>
#include <unordered_set>
#include <vector>

// O(n^2): compare every pair
long long nestedOps(const std::vector<int>& a) {
    long long ops = 0;
    for (size_t i = 0; i < a.size(); ++i)
        for (size_t j = i + 1; j < a.size(); ++j) {
            ++ops;
            if (a[i] == a[j]) return ops;
        }
    return ops;
}

// O(n): remember what we have seen
long long setOps(const std::vector<int>& a) {
    long long ops = 0;
    std::unordered_set<int> seen;
    for (int x : a) {
        ++ops;
        if (!seen.insert(x).second) return ops; // insert reports a duplicate
    }
    return ops;
}

int main() {
    for (int n : {1000, 10000}) {
        std::vector<int> a(n);
        std::iota(a.begin(), a.end(), 0); // 0, 1, 2, ...: no duplicates, the worst case
        std::cout << "n=" << n << "  nested=" << nestedOps(a) << "  set=" << setOps(a) << '\n';
    }
}
Outputcompiled & run with real C++
n=1000  nested=499500  set=1000
n=10000  nested=49995000  set=10000

Ten times the input: the nested version does a hundred times the work, the set version ten times. At n = 106 the nested version would need half a trillion comparisons.

Your turn

Add a third function that sorts a copy and compares neighbours. Count its operations as n for the neighbour pass. What is its Big-O, including the sort?

04

Pattern 1: two pointers

Signal: a sorted array (or a string) and a question about pairs, or "do it in place". Put one index at each end and move them towards each other based on a comparison. Each step rules out a whole row of candidate pairs, so an O(n²) "try every pair" becomes O(n) with O(1) extra memory.

C++main.cpp
#include <iostream>
#include <optional>
#include <utility>
#include <vector>

// sorted input: indices of two numbers that add up to target
std::optional<std::pair<int, int>> pairSum(const std::vector<int>& a, int target) {
    int i = 0, j = static_cast<int>(a.size()) - 1;
    while (i < j) {
        int sum = a[i] + a[j];
        if (sum == target) return std::pair{i, j};
        if (sum < target) ++i; // need bigger: move the left pointer up
        else --j;              // need smaller: move the right pointer down
    }
    return std::nullopt;
}

int main() {
    std::vector<int> a{1, 3, 4, 6, 8, 11};
    for (int target : {10, 2}) {
        if (auto p = pairSum(a, target))
            std::cout << target << ": (" << p->first << ", " << p->second << ")\n";
        else
            std::cout << target << ": none\n";
    }
}
Outputcompiled & run with real C++
10: (2, 3)
2: none
Your turn

Write std::vector<int> sortedSquares(const std::vector<int>& a) for a sorted vector that may hold negatives: {-4, -1, 0, 3, 10} gives {0, 1, 9, 16, 100}. Fill the result from the back, each time taking the larger of std::abs(a[i]) and std::abs(a[j]).

VisualizepairSum({1, 3, 4, 6, 8, 11}, 10)Step 1 / 7
int i = 0, j = static_cast<int>(a.size()) - 1;
while (i < j) {
int sum = a[i] + a[j];
if (sum == target) return std::pair{i, j};
if (sum < target) ++i;
else --j;
}
Line 1

Pointers at both ends.

Variables now
i0
j5
All 7 steps as a table
StepLineWhat happenedVariables now
11Pointers at both ends.i = 0 j = 5
231 + 11 = 12, too big.sum = 12
36Nothing can pair with 11 any more (1 is the smallest partner), so drop it.j = 4
431 + 8 = 9, too small.sum = 9
55Nothing can pair with 1 (8 is now the largest partner), so drop it.i = 1
633 + 8 = 11, too big: drop 8. Then 3 + 6 = 9, too small: drop 3.i = 2 j = 3
744 + 6 = 10: found, after 5 checks instead of up to 15 pairs.
05

Pattern 2: sliding window

Signal: "longest / shortest / best contiguous subarray or substring such that…". Grow a window by moving its right edge; when the window breaks the rule, shrink it from the left. Each index enters and leaves the window at most once, so the whole scan is O(n).

C++main.cpp
#include <algorithm>
#include <array>
#include <iostream>
#include <string>
#include <vector>

// length of the longest substring with no repeated character
int longestUnique(const std::string& s) {
    std::array<int, 256> lastSeen;
    lastSeen.fill(-1);
    int best = 0, left = 0;
    for (int right = 0; right < static_cast<int>(s.size()); ++right) {
        unsigned char c = s[right];
        if (lastSeen[c] >= left) left = lastSeen[c] + 1; // jump past the repeat
        lastSeen[c] = right;
        best = std::max(best, right - left + 1);
    }
    return best;
}

// fixed-size window: biggest sum of any k consecutive numbers
int maxSumK(const std::vector<int>& a, int k) {
    int sum = 0;
    for (int i = 0; i < k; ++i) sum += a[i];
    int best = sum;
    for (size_t i = k; i < a.size(); ++i) {
        sum += a[i] - a[i - k]; // slide: add the new, drop the old
        best = std::max(best, sum);
    }
    return best;
}

int main() {
    std::cout << longestUnique("abcabcbb") << ' ' << longestUnique("bbbb") << ' '
              << longestUnique("pwwkew") << '\n';
    std::cout << maxSumK({2, 1, 5, 1, 3, 2}, 3) << '\n';
}
Outputcompiled & run with real C++
3 1 3
9

A fixed std::array<int, 256> indexed by byte replaces a hash map for ASCII input: faster and allocation-free. The check lastSeen[c] >= left ignores repeats that are already outside the window.

Your turn

Write int minLengthAtLeast(const std::vector<int>& a, int target): the shortest contiguous run of positive numbers whose sum is at least target (0 if none). {2, 3, 1, 2, 4, 3} with target 7 gives 2.

06

Pattern 3: unordered_map and unordered_set lookups

Signal: "find two that…", "count how many…", "group by…", "first duplicate". Trade memory for time: remember what you have seen in a hash map keyed by the thing you will look up later. Use find or contains (C++20) to look up; operator[] silently inserts a default value for a missing key.

C++main.cpp
#include <algorithm>
#include <iostream>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

// unsorted input: indices of two numbers that add up to target
std::optional<std::pair<int, int>> twoSum(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> indexOf;
    for (int i = 0; i < static_cast<int>(nums.size()); ++i) {
        if (auto it = indexOf.find(target - nums[i]); it != indexOf.end())
            return std::pair{it->second, i};
        indexOf[nums[i]] = i;
    }
    return std::nullopt;
}

// group words that are anagrams of each other, in first-seen order
std::vector<std::vector<std::string>> groupAnagrams(const std::vector<std::string>& words) {
    std::unordered_map<std::string, size_t> groupOf;
    std::vector<std::vector<std::string>> groups;
    for (const auto& w : words) {
        std::string key = w;
        std::sort(key.begin(), key.end());
        auto [it, inserted] = groupOf.try_emplace(key, groups.size());
        if (inserted) groups.emplace_back();
        groups[it->second].push_back(w);
    }
    return groups;
}

int main() {
    auto p = twoSum({2, 7, 11, 15}, 9);
    std::cout << p->first << ' ' << p->second << '\n';
    for (const auto& g : groupAnagrams({"eat", "tea", "tan", "ate", "nat", "bat"})) {
        for (size_t i = 0; i < g.size(); ++i) std::cout << (i ? "," : "") << g[i];
        std::cout << '\n';
    }
}
Outputcompiled & run with real C++
0 1
eat,tea,ate
tan,nat
bat

Iterating an unordered_map gives no useful order, so the groups live in a vector and the map only stores each group's index. try_emplace inserts only if the key is new and reports which happened.

Your turn

Write int firstUniqueChar(const std::string& s): the index of the first character that appears exactly once, or -1. Two passes: count, then find. "leetcode" gives 0, "aabb" gives -1.

07

Pattern 4: stack

Signal: nesting (brackets, tags, paths), "undo", or "the next greater / smaller element". A monotonic stack keeps indexes whose values are still waiting for an answer; each new value pops every waiting index it answers. Every index is pushed and popped once: O(n). A std::vector with push_back/back/pop_back is the usual stack.

C++main.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

// for each day, how many days until a warmer temperature (0 if never)
std::vector<int> daysUntilWarmer(const std::vector<int>& temps) {
    std::vector<int> answer(temps.size(), 0);
    std::vector<int> waiting; // indexes, temperatures decreasing from bottom to top
    for (int i = 0; i < static_cast<int>(temps.size()); ++i) {
        while (!waiting.empty() && temps[waiting.back()] < temps[i]) {
            answer[waiting.back()] = i - waiting.back();
            waiting.pop_back();
        }
        waiting.push_back(i);
    }
    return answer;
}

// simplify a Unix path: /a/./b/../../c/ -> /c
std::string simplifyPath(const std::string& path) {
    std::vector<std::string> stack;
    std::stringstream in(path);
    std::string part;
    while (std::getline(in, part, '/')) {
        if (part.empty() || part == ".") continue;
        if (part == "..") { if (!stack.empty()) stack.pop_back(); }
        else stack.push_back(part);
    }
    std::string out;
    for (const auto& p : stack) out += "/" + p;
    return out.empty() ? "/" : out;
}

int main() {
    for (int d : daysUntilWarmer({73, 74, 75, 71, 69, 72, 76, 73})) std::cout << d << ' ';
    std::cout << '\n' << simplifyPath("/a/./b/../../c/") << '\n' << simplifyPath("/../") << '\n';
}
Outputcompiled & run with real C++
1 1 4 2 1 1 0 0 
/c
/
Your turn

Evaluate Reverse Polish Notation: evalRpn({"2", "1", "+", "3", "*"}) is 9. Push numbers (std::stoi); on an operator, pop two, apply, push the result.

08

Pattern 5: recursion and backtracking

Signal: "all combinations", "all permutations", "every way to…", and small n (under about 20). Build a candidate one choice at a time in a vector; recurse; then undo the choice with pop_back and try the next one. Passing the candidate by reference and undoing is what keeps backtracking allocation-light.

C++main.cpp
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

std::vector<std::vector<int>> combinationSum(const std::vector<int>& candidates, int target) {
    std::vector<std::vector<int>> out;
    std::vector<int> current;
    std::function<void(size_t, int)> walk = [&](size_t start, int remaining) {
        if (remaining == 0) { out.push_back(current); return; }
        for (size_t i = start; i < candidates.size(); ++i) {
            if (candidates[i] > remaining) continue;
            current.push_back(candidates[i]);          // choose
            walk(i, remaining - candidates[i]);        // explore (reuse allowed: i, not i + 1)
            current.pop_back();                        // un-choose
        }
    };
    walk(0, target);
    return out;
}

int main() {
    for (const auto& combo : combinationSum({2, 3, 6, 7}, 7)) {
        for (int x : combo) std::cout << x << ' ';
        std::cout << '\n';
    }

    std::vector<int> v{1, 2, 3};
    do {
        for (int x : v) std::cout << x;
        std::cout << ' ';
    } while (std::next_permutation(v.begin(), v.end()));
    std::cout << '\n';
}
Outputcompiled & run with real C++
2 2 3 
7 
123 132 213 231 312 321 

A lambda cannot call itself by name, so the recursive helper is stored in a std::function it captures by reference. For permutations the standard library already has std::next_permutation, which walks them in sorted order.

Your turn

Write subsets(const std::vector<char>& items) that prints every subset of {'a', 'b', 'c'} with the same choose / explore / un-choose shape. There are 8.

09

Pattern 6: sort first

Signal: intervals, "closest", "meeting rooms", "can these be scheduled", or any problem that gets easy once things are in order. Paying O(n log n) for std::sort often turns the rest into a single O(n) pass.

C++main.cpp
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>

using Interval = std::pair<int, int>;

std::vector<Interval> mergeIntervals(std::vector<Interval> intervals) {
    std::sort(intervals.begin(), intervals.end()); // pairs sort by first, then second
    std::vector<Interval> merged;
    for (const auto& [start, end] : intervals) {
        if (!merged.empty() && start <= merged.back().second)
            merged.back().second = std::max(merged.back().second, end); // overlap: extend
        else
            merged.push_back({start, end});                             // gap: new interval
    }
    return merged;
}

int main() {
    for (const auto& [s, e] : mergeIntervals({{8, 10}, {1, 3}, {2, 6}, {15, 18}, {17, 20}}))
        std::cout << '[' << s << ',' << e << "] ";
    std::cout << '\n';
    for (const auto& [s, e] : mergeIntervals({{1, 4}, {4, 5}}))
        std::cout << '[' << s << ',' << e << "] ";
    std::cout << '\n';
}
Outputcompiled & run with real C++
[1,6] [8,10] [15,20] 
[1,5] 

The parameter is taken by value on purpose: the function needs its own copy to sort, and a caller passing a temporary pays nothing extra because it is moved in.

Your turn

Meeting rooms: given meetings as {start, end}, return the fewest rooms needed. Sort the starts and the ends separately, then walk both with two pointers.

10

Pattern 7: BFS and DFS on a grid

Signal: a grid or map ("islands", "rooms", "shortest path in a maze"), or anything described as connections. Treat each cell as a node with up to four neighbours. DFS answers "which cells connect?"; BFS with a std::queue answers "how many steps at minimum?".

C++main.cpp
#include <array>
#include <iostream>
#include <queue>
#include <string>
#include <tuple>
#include <vector>

const std::array<std::pair<int, int>, 4> dirs{{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}};

void sink(std::vector<std::string>& grid, int r, int c) {
    if (r < 0 || c < 0 || r >= static_cast<int>(grid.size()) || c >= static_cast<int>(grid[0].size())) return;
    if (grid[r][c] != '#') return;
    grid[r][c] = '.'; // mark visited by sinking the land
    for (auto [dr, dc] : dirs) sink(grid, r + dr, c + dc);
}

int countIslands(std::vector<std::string> grid) { // copy: we modify it
    int islands = 0;
    for (int r = 0; r < static_cast<int>(grid.size()); ++r)
        for (int c = 0; c < static_cast<int>(grid[r].size()); ++c)
            if (grid[r][c] == '#') { ++islands; sink(grid, r, c); }
    return islands;
}

int shortestSteps(const std::vector<std::string>& maze) {
    int rows = maze.size(), cols = maze[0].size();
    std::vector<std::vector<int>> dist(rows, std::vector<int>(cols, -1));
    std::queue<std::pair<int, int>> q;
    q.push({0, 0});
    dist[0][0] = 0;
    while (!q.empty()) {
        auto [r, c] = q.front();
        q.pop();
        if (r == rows - 1 && c == cols - 1) return dist[r][c];
        for (auto [dr, dc] : dirs) {
            int nr = r + dr, nc = c + dc;
            if (nr < 0 || nc < 0 || nr >= rows || nc >= cols) continue;
            if (maze[nr][nc] == '#' || dist[nr][nc] != -1) continue;
            dist[nr][nc] = dist[r][c] + 1;
            q.push({nr, nc});
        }
    }
    return -1;
}

int main() {
    std::cout << countIslands({"##..#", "#...#", "..#..", ".....", "##.##"}) << '\n';
    std::cout << shortestSteps({"..#.", "#...", "..#.", ".#.."}) << '\n';
}
Outputcompiled & run with real C++
5
6

The dist table does double duty: -1 means "not visited yet", and any other value is the step count from the start.

Your turn

Change countIslands to return the size of the biggest island instead: make sink return the number of cells it sank.

11

Pattern 8: DP-lite

Signal: "how many ways", "minimum cost", "maximum value", where the answer for n depends on the answers for smaller n. Write the one-sentence meaning of dp[i], the base case, and the step from smaller answers. Often you only need the last one or two values, not a whole table.

C++main.cpp
#include <algorithm>
#include <iostream>
#include <vector>

// ways to climb n stairs taking 1 or 2 steps: ways(n) = ways(n-1) + ways(n-2)
long long climbStairs(int n) {
    long long a = 1, b = 1;
    for (int i = 2; i <= n; ++i) {
        long long next = a + b;
        a = b;
        b = next;
    }
    return b;
}

// house robber: max sum with no two adjacent houses
int rob(const std::vector<int>& houses) {
    int take = 0, skip = 0; // best total if we take / skip the previous house
    for (int h : houses) {
        int newTake = skip + h;
        skip = std::max(take, skip);
        take = newTake;
    }
    return std::max(take, skip);
}

// unique paths moving only right or down through a rows x cols grid
long long uniquePaths(int rows, int cols) {
    std::vector<long long> row(cols, 1);
    for (int r = 1; r < rows; ++r)
        for (int c = 1; c < cols; ++c) row[c] += row[c - 1]; // above + left
    return row[cols - 1];
}

int main() {
    std::cout << climbStairs(5) << ' ' << climbStairs(10) << '\n';
    std::cout << rob({2, 7, 9, 3, 1}) << ' ' << rob({2, 1, 1, 2}) << '\n';
    std::cout << uniquePaths(3, 7) << '\n';
}
Outputcompiled & run with real C++
8 89
12 4
28

uniquePaths keeps one row instead of a full table: before the update, row[c] still holds the value from the row above, so row[c] += row[c - 1] is "above plus left".

Your turn

Write int longestIncreasing(const std::vector<int>& xs), the length of the longest strictly increasing subsequence, with dp[i] = the longest one ending at i. {10, 9, 2, 5, 3, 7, 101, 18} gives 4.

12

Choosing the pattern

The problem says…TryTypical cost
sorted array, pairs, "in place"Two pointersO(n)
contiguous subarray / substring, "longest", "at most k"Sliding windowO(n)
"find two", "count", "group", "seen before"unordered_map / unordered_setO(n) average
nesting, "next greater", undoStack (a vector)O(n)
"all combinations / permutations", n ≤ 20BacktrackingO(2n) or O(n!)
intervals, schedules, "closest"Sort firstO(n log n)
grid, maze, connections, "minimum steps"BFS / DFSO(rows × cols)
"how many ways", "min cost", "max value"DPO(n) to O(n²)
Say it out loud, and mind the C++ traps
Name the pattern and its cost before coding it. Then watch the traps interviewers look for in C++: comparing int with size() (unsigned) in loop conditions, operator[] inserting into a map, signed overflow in sums, recursion deep enough to overflow the stack, and holding a reference or iterator into a vector across a push_back.
Two pointers
Two indexes moving towards each other (or in the same direction) to avoid checking every pair.
Sliding window
A contiguous range grown from the right and shrunk from the left, so each element is visited at most twice.
Monotonic stack
A stack kept in increasing or decreasing order, used for "next greater / smaller element" questions.
Backtracking
Building candidates one choice at a time, recursing, then undoing the choice to try the next one.
DP state
The one-sentence meaning of dp[i]: what sub-problem each table cell answers.
Quick check

The problem: "Given up to 100,000 integers, return the length of the longest contiguous subarray whose sum is at most k." All numbers are positive. Which approach fits?

Frequently asked questions

Is C++ good for coding interviews?
Yes. C++ is one of the most popular interview and competitive-programming languages: it is fast, and the STL provides sort, binary search, hash maps, heaps and permutations out of the box. Know its traps, such as signed and unsigned comparisons, map operator[] inserting defaults, integer overflow and iterator invalidation.
What are the most common coding interview patterns?
Eight patterns cover most problems: two pointers, sliding window, hash map lookups, stack, recursion and backtracking, sorting first, BFS and DFS, and simple dynamic programming. Recognising the signal words for each one matters more than memorising individual problems.
How do I know which algorithm a problem needs?
Look at the input limits. Up to about 20 items allows trying every combination; a few thousand allows O(n²); around a million needs O(n log n) or O(n). Then match the wording: "contiguous" suggests a sliding window, "sorted" suggests two pointers or binary search, "how many ways" suggests DP.

Finish the C++ handbook, then get hired

Sit the exam for your certificate, run your resume through the ATS checker, and see the jobs that ask for exactly this.

Check my resume
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.