Preparing for C++ Coding Interviews: 7 Problem Types, Must-Know STL and a Day-Of Checklist

Key takeaways

C++ coding test interview preparation guide. Explains the seven most common problem types, essential STL, time complexity budgets, fast I/O and why it works, the non-obvious mistakes that cause TLE, wrong answers and memory limit errors, and an interview day checklist.

Introduction: C++ Coding Test Interview, What to Prepare?

Coding test interviews (exams where you solve algorithm/data structure problems in code within a time limit) are typically conducted on a whiteboard or online IDE where you solve algorithm problems in 30-60 minutes. Difficulty varies by company, but they evaluate data structure/algorithm fundamentals and C++ STL usage skills. This article is an interview preparation guide summarizing frequently appearing problem types, essential STL functions, and the C++-specific habits that decide whether a correct idea actually gets accepted.

What this article covers:

  • 7 frequently appearing algorithm types in coding test interviews
  • Essential C++ STL functions summary (vector, map, set, algorithm)
  • Time complexity analysis and optimization strategies
  • Fast I/O, edge cases and readability, and why each matters
  • Mistakes behind TLE, wrong answers and memory limit errors that are specific to C++
  • Interview day checklist

The algorithm types and preparation flow frequently appearing in coding tests are summarized below.

flowchart LR
  subgraph type[Types]
    S[Sort·Search]
    D[DP]
    G[Graph]
    T[Tree]
  end
  subgraph prep[Preparation]
    STL[STL Usage]
    IO[I/O Optimization]
    TC[Time Complexity]
  end
  type --> prep

7 Frequently Appearing Algorithm Types

Sorting and Searching

Representative Problems:

  • Find specific value after sorting array (binary search)
  • Find K-th largest/smallest element
  • Intersection/union of two arrays

Key STL:

  • std::sort(v.begin(), v.end())
  • std::binary_search, std::lower_bound, std::upper_bound
  • std::nth_element (K-th element)

When to use sorting?
Sorting is an expensive operation at O(N log N), but after sorting you can use efficient algorithms like binary search (O(log N)) and two pointers (O(N)). Problems that would require brute force O(N²) can be solved in O(N log N) with sort + binary search.

lower_bound vs upper_bound:

  • lower_bound(x): First position ≥ x
  • upper_bound(x): First position > x
  • Difference: upper_bound - lower_bound = count of x

Example Code (copy-paste and run with g++ -std=c++17 -o binsearch_demo binsearch_demo.cpp && ./binsearch_demo):

// Copy-paste and run: g++ -std=c++17 -o binsearch_demo binsearch_demo.cpp && ./binsearch_demo
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> v = {3, 1, 4, 1, 5};
    std::sort(v.begin(), v.end());  // {1, 1, 3, 4, 5}
    bool found = std::binary_search(v.begin(), v.end(), 3);
    auto it = std::lower_bound(v.begin(), v.end(), 3);
    std::cout << found << " " << (it - v.begin()) << "\n";  // 1 2
    return 0;
}

Execution Result: Outputs 1 2 (found and index) on one line.

HashMap (Frequency, Duplicate Check)

Representative Problems:

  • Find duplicate elements in array
  • Check if two strings are anagrams
  • Count subarrays with sum K (prefix sum + hashmap)

Key STL:

  • std::unordered_map<int, int> (O(1) average access)
  • std::unordered_set<int> (duplicate check)

HashMap Core:
HashMap allows insert/delete/search in O(1) average time. Problems that would require nested loops O(N²) can often be solved in O(N).

Two Sum Problem Example:
“Find two elements that sum to K in array”

Brute Force (O(N²)):

for (int i = 0; i < n; ++i) {
    for (int j = i+1; j < n; ++j) {
        if (arr[i] + arr[j] == K) { /* Found */ }
    }
}

HashMap Method (O(N)):

std::unordered_set<int> seen;
for (int x : arr) {
    if (seen.count(K - x)) { /* Found */ }
    seen.insert(x);
}

Example Code:

std::unordered_map<int, int> freq;
for (int x : arr) {
    freq[x]++;
}

// Most frequent element
int maxFreq = 0, result = 0;
for (auto& [num, cnt] : freq) {
    if (cnt > maxFreq) {
        maxFreq = cnt;
        result = num;
    }
}

Two Pointers

Representative Problems:

  • Find two elements that sum to K in sorted array
  • Maximum length of subarray with sum ≤ K
  • Palindrome check

Key Idea:

  • Move left and right pointers simultaneously to solve in O(N)

Example Code:

// Two elements that sum to target in sorted array
std::vector<int> v = {1, 2, 3, 4, 5};
int target = 7;
int left = 0, right = v.size() - 1;

while (left < right) {
    int sum = v[left] + v[right];
    if (sum == target) {
        std::cout << v[left] << ", " << v[right] << '\n';
        break;
    } else if (sum < target) {
        left++;
    } else {
        right--;
    }
}

v.size() - 1 deserves a second look: size() returns an unsigned size_t, so on an empty vector v.size() - 1 wraps to a huge number. Here it is stored into an int and the loop is skipped, but in for (size_t i = 0; i < v.size() - 1; ++i) the same expression turns an empty input into an out-of-bounds crash. Check v.empty() first or compare i + 1 < v.size().

A close relative is the fixed-size sliding window: instead of recomputing each window’s sum in O(K), add the element entering the window and subtract the one leaving, for O(N) total.

long long sum = 0;
for (int i = 0; i < k; ++i) sum += arr[i];
long long best = sum;
for (int i = k; i < n; ++i) {
    sum += arr[i] - arr[i - k];  // slide one step right
    best = std::max(best, sum);
}

Dynamic Programming (DP)

DP (Dynamic Programming—algorithm technique that stores answers to small subproblems and reuses them to solve larger problems) representative problems are:

Representative Problems:

  • Fibonacci sequence
  • Longest Increasing Subsequence (LIS)
  • Knapsack problem
  • Shortest path (DP + graph)

Key Idea:

  • Memoization: Store calculated values in array for reuse

Why use DP?
Recursive solutions have duplicate calculations. For example, when calculating fib(5):

  • fib(5) = fib(4) + fib(3)
  • fib(4) = fib(3) + fib(2)
  • fib(3) is calculated twice.

Calculating fib(50) with pure recursion requires billions of function calls, but with DP only 50 calculations.

Top-Down vs Bottom-Up:

  • Top-Down (Memoization): Recursion + cache. Calculates only needed values.
  • Bottom-Up (Tabulation): Loop from small problems sequentially. Usually faster.

Example Code (Fibonacci):

std::vector<long long> dp(93, -1);  // fib(92) is the largest that fits in long long

long long fib(int n) {
    if (n <= 1) return n;
    if (dp[n] != -1) return dp[n];  // Already calculated
    return dp[n] = fib(n-1) + fib(n-2);
}

Bottom-Up Method (faster):

std::vector<long long> dp(93);
dp[0] = 0; dp[1] = 1;
for (int i = 2; i < 93; ++i) {
    dp[i] = dp[i-1] + dp[i-2];
}

The size is not arbitrary: fib(93) exceeds the long long range, and signed overflow is undefined behavior, not a wraparound you can reason about. That is why DP problems with large answers almost always say “print the answer modulo 1,000,000,007”—take % MOD at every addition, not once at the end.

Top-down memoization has a C++-specific cost worth knowing: recursion depth. A memoized recursion over N = 10⁶ states can overflow the default stack (often 1–8 MB, and some judges do not raise it) before it ever returns, so for large N prefer the bottom-up loop.

One more DP classic (maximum subarray sum, Kadane):

long long best = arr[0], cur = arr[0];  // start from arr[0], not 0, so all-negative input works
for (int i = 1; i < n; ++i) {
    cur = std::max<long long>(arr[i], cur + arr[i]);
    best = std::max(best, cur);
}

Initializing best to 0 is the classic wrong answer here: if every element is negative, the correct answer is the largest (least negative) element, not 0.

Graph Traversal (BFS, DFS)

Graph (data structure of vertices and edges. Example: cities=vertices, roads=edges on map) traversal representative problems are:

Representative Problems:

  • Maze shortest path (BFS)
  • Count connected components (DFS/BFS)
  • Cycle detection
  • Topological sort

Key STL:

  • std::queue<int> (BFS)
  • std::stack<int> or recursion (DFS)
  • std::vector<std::vector<int>> (adjacency list)

BFS Example:

std::vector<std::vector<int>> graph(n);
std::vector<bool> visited(n, false);
std::queue<int> q;

q.push(start);
visited[start] = true;

while (!q.empty()) {
    int node = q.front();
    q.pop();
    
    for (int neighbor : graph[node]) {
        if (!visited[neighbor]) {
            visited[neighbor] = true;
            q.push(neighbor);
        }
    }
}

Greedy

Representative Problems:

  • Meeting room assignment (activity selection problem)
  • Coin change (specific conditions)
  • Minimum spanning tree (Kruskal, Prim)

Key Idea:

  • Best choice at each moment leads to global optimum

Example Code (Meeting Room Assignment):

struct Meeting {
    int start, end;
};

std::vector<Meeting> meetings = {{1, 3}, {2, 4}, {3, 5}};

// Sort by end time; break ties by start time
std::sort(meetings.begin(), meetings.end(), [](const Meeting& a, const Meeting& b) {
    if (a.end != b.end) return a.end < b.end;
    return a.start < b.start;
});

int count = 0, lastEnd = 0;
for (auto& m : meetings) {
    if (m.start >= lastEnd) {
        count++;
        lastEnd = m.end;
    }
}

The tie-break looks cosmetic but changes the answer when zero-length meetings are allowed. With (1, 2) and (2, 2), sorting by end time alone may put (2, 2) first; it is taken, lastEnd becomes 2, and (1, 2) is rejected—one meeting instead of two. Baekjoon 1931 is the well-known problem where this exact case separates accepted from wrong answer. Two other sort details matter: take lambda parameters by const& (copying structs on every comparison adds up for 10⁵ elements), and make the comparator a strict “less than”—returning a.end <= b.end violates strict weak ordering, which is undefined behavior and can crash std::sort on inputs with many equal keys.

Backtracking

Representative Problems:

  • N-Queen
  • Generate permutations/combinations
  • Sudoku solver

Key Idea:

  • Try all cases recursively, but backtrack immediately if condition not met

Example Code (Permutation):

void permute(std::vector<int>& nums, int start) {
    if (start == nums.size()) {
        // One permutation complete
        for (int x : nums) std::cout << x << ' ';
        std::cout << '\n';
        return;
    }
    
    for (int i = start; i < nums.size(); ++i) {
        std::swap(nums[start], nums[i]);
        permute(nums, start + 1);
        std::swap(nums[start], nums[i]);  // Backtracking
    }
}

Or using STL:

std::vector<int> v = {1, 2, 3};
std::sort(v.begin(), v.end());
do {
    for (int x : v) std::cout << x << ' ';
    std::cout << '\n';
} while (std::next_permutation(v.begin(), v.end()));

Essential C++ STL Functions Summary

vector

std::vector<int> v = {1, 2, 3};
v.push_back(4);           // Add to end
v.pop_back();             // Remove from end
v.size();                 // Size
v.empty();                // Is empty?
v.clear();                // Clear all
v[0];                     // Index access (no range check)
v.at(0);                  // Range check (throws exception)

map (Sorted Keys)

std::map<std::string, int> m;
m["apple"] = 1;
m["banana"] = 2;

if (m.count("apple")) {   // Key exists?
    std::cout << m["apple"] << '\n';
}

for (auto& [key, val] : m) {  // Iterate in key order
    std::cout << key << ": " << val << '\n';
}

unordered_map (HashMap, O(1) Average)

std::unordered_map<int, int> freq;
for (int x : arr) {
    freq[x]++;
}

set (Unique Sorted Elements)

std::set<int> s = {3, 1, 4, 1, 5};  // {1, 3, 4, 5}
s.insert(2);
s.erase(3);
bool exists = s.count(4);  // 1 (exists) or 0 (not exists)

Choosing a container

RequirementRecommended ContainerTime Complexity
Order preservation, fast accessvectorAccess O(1), push/pop at end O(1), insert/erase elsewhere O(N)
Unique set, sortedsetInsert/Delete/Search O(log N)
Unique set, unorderedunordered_setAverage O(1)
Key-value pairs, sortedmapO(log N)
Key-value pairs, unorderedunordered_mapAverage O(1)
Queue (FIFO)queueO(1)
Stack (LIFO)stackO(1)
Priority queuepriority_queuePush/Pop O(log N), Top O(1)

std::priority_queue is a max-heap by default. For Dijkstra and other “smallest first” problems, declare std::priority_queue<T, std::vector<T>, std::greater<T>>; forgetting this silently processes the largest distance first and produces wrong answers rather than a crash.

algorithm and numeric headers

#include <algorithm>
#include <numeric>

std::sort(v.begin(), v.end());                                   // Ascending
std::sort(v.begin(), v.end(), std::greater<>());                 // Descending
std::reverse(v.begin(), v.end());

auto it = std::find(v.begin(), v.end(), 42);                     // O(N) linear search
if (it != v.end()) { /* Found */ }
bool found = std::binary_search(v.begin(), v.end(), 42);         // O(log N), requires sorted input
auto cnt = std::count(v.begin(), v.end(), 42);

int maxVal = *std::max_element(v.begin(), v.end());              // dereferencing end() on empty v is UB
int minVal = *std::min_element(v.begin(), v.end());

long long total = std::accumulate(v.begin(), v.end(), 0LL);      // 0LL, not 0 (see below)

std::sort(v.begin(), v.end());                                   // Remove duplicates: sort first,
v.erase(std::unique(v.begin(), v.end()), v.end());               // then erase the tail unique() leaves

std::next_permutation(v.begin(), v.end());                       // Next lexicographic permutation
std::nth_element(v.begin(), v.begin() + k, v.end());             // k-th smallest (0-based) in average O(N)

std::accumulate deserves the comment. Its accumulator type is the type of the initial value, not of the elements. With 0 the running sum is an int, so summing 10⁵ values of up to 10⁹ overflows even if you assign the result to a long long. Passing 0LL makes the whole accumulation 64-bit. The same trap applies to std::accumulate over double values with an initial 0: every partial sum gets truncated to an integer.

std::unique does not shrink the container: it moves the unique elements to the front and returns the new logical end, which is why it is paired with erase. It also only removes adjacent duplicates, so it needs sorted input for set-like behavior.

string Functions

std::string s = "hello";
s.size();                     // Length
s.substr(1, 3);               // "ell" (start index, length) - allocates and copies
s.find("ll");                 // 2 (position), or std::string::npos if not found
s.rfind("l");                 // 3 (search from back)

// String → number
int num = std::stoi("123");
long long big = std::stoll("123456789012");

// Number → string
std::string str = std::to_string(123);

Build strings with s += c or s.push_back(c), not s = s + c. The latter creates a full copy on every iteration, so a loop that appends 10⁵ characters copies about 5×10⁹ bytes in total.


Time Complexity Analysis and Optimization

Frequently Appearing Time Complexities

ComplexityN=100N=10,000N=1,000,000Example Algorithm
O(1)111HashMap access
O(log N)71320Binary search
O(N)10010,0001,000,000Linear search, traversal
O(N log N)700130,00020,000,000Sorting (merge sort)
O(N²)10,000100,000,0001,000,000,000,000Nested loops
O(2^N)1.3×10³⁰--Brute force (backtracking)

Time Limit and Complexity

1 second limit roughly allows:

  • O(N): N ≤ 10⁸
  • O(N log N): N ≤ 10⁶
  • O(N²): N ≤ 10⁴
  • O(N³): N ≤ 500

Why these standards?
Modern computers can perform about 10⁸~10⁹ basic operations per second. But real algorithms have overhead from function calls, memory access, branches, so we safely assume 10⁸.

Concrete Example:

  • N=10⁶, O(N²) → 10¹² operations → 1000 seconds (time exceeded)
  • N=10⁶, O(N log N) → 2×10⁷ operations → 0.2 seconds (pass)

Common interview mistake: Apply O(N²) algorithm to N=10⁶ input → time exceeded

Tip: Look at N range in problem and reverse-calculate allowed complexity.

  • N ≤ 10 → O(N!) possible (brute force)
  • N ≤ 20 → O(2^N) possible (bitmask DP)
  • N ≤ 500 → O(N³) possible (Floyd-Warshall)
  • N ≤ 10⁴ → O(N²) possible (nested loops)
  • N ≤ 10⁶ → O(N log N) needed (sorting, segment tree)
  • N ≤ 10⁸ → O(N) needed (linear algorithm)

Optimization Strategies

1. Look for hidden O(N) costs inside the loop body:

A loop that looks O(N) is really O(N²) if its body does linear work. The usual suspects in C++:

// ❌ Each of these is O(N) per call
v.erase(v.begin());                 // shifts every remaining element
s = s + c;                          // copies the whole string
dfs(graph, visited, node);          // if graph/visited are taken by value, each call copies them
auto sub = s.substr(i);             // allocates and copies the suffix

// ✅ O(1) alternatives
dq.pop_front();                     // std::deque, or keep a head index into the vector
s += c;
void dfs(const std::vector<std::vector<int>>& graph, std::vector<bool>& visited, int node);
std::string_view sub = std::string_view(s).substr(i);  // no copy (C++17)

The pass-by-value case is the one that has cost me the most submissions: a DFS declared as void dfs(vector<vector<int>> graph, int node) passed every sample instantly and then hit TLE (or MLE, since each recursion frame held its own copy of the graph) on the first large test. Nothing in the algorithm was wrong; one missing & made each call copy the adjacency list. Calling v.size() in a loop condition, by contrast, is not worth worrying about: it is O(1) and the compiler hoists it anyway.

2. HashMap to reduce O(N²) → O(N):

// ❌ O(N²)
for (int i = 0; i < n; ++i) {
    for (int j = i+1; j < n; ++j) {
        if (arr[i] + arr[j] == target) { /* ... */ }
    }
}

// ✅ O(N)
std::unordered_set<int> seen;
for (int x : arr) {
    if (seen.count(target - x)) {
        // Found
    }
    seen.insert(x);
}

3. Use sorting:

Sorting costs O(N log N), but enables binary search/two pointers to reduce O(N²) → O(N log N) or O(N).


Fast I/O, Edge Cases and Readability

I/O Optimization

In online judges like Baekjoon/Programmers, heavy I/O can cause time limit exceeded even when the algorithm is right.

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

int main() {
    ios_base::sync_with_stdio(false);  // stop synchronizing C++ streams with C stdio
    cin.tie(nullptr);                  // stop flushing cout before every cin read

    int n;
    cin >> n;
    vector<int> v(n);
    for (int& x : v) cin >> x;         // read straight into the elements

    cout << n << '\n';    // ✅ newline only
    // cout << n << endl; // ❌ newline + flush on every line
    return 0;
}

Why each line matters

sync_with_stdio(false): by default the C++ streams (cin, cout) stay synchronized with C’s stdio (scanf, printf) so that mixed output appears in order. With common implementations such as GCC’s libstdc++, that synchronization routes each stream operation through C stdio instead of letting cin/cout use their own buffers, which is what makes them slow on large inputs. Turning it off lets them buffer independently. The price: after this call, do not mix cin/cout with scanf/printf/puts in the same program. Output can appear out of order, and input can be consumed by the wrong buffer.

cin.tie(nullptr): by default cin is tied to cout, so every read flushes pending output first. That is what makes prompts appear before the user types, and it means that for 10⁵ alternating reads and writes you pay 10⁵ flushes. Untying removes those flushes.

endl vs '\n': endl writes a newline and flushes. With 10⁵ lines of output that is 10⁵ write system calls instead of a handful of buffered ones. '\n' just appends a newline; the buffer is flushed when it fills or when the program exits normally.

How much this matters depends on the judge and the data size, but on problems with around 10⁵–10⁶ numbers of input or output, default streams plus endl versus the setup above is often exactly the difference between TLE and a comfortable pass.

Interactive problems are the exception. When the judge replies to your output, you must flush after each query (cout << q << endl; or cout.flush()). Otherwise your program waits for an answer to a query that is still sitting in your own buffer, and the verdict is TLE or “idleness limit exceeded.”

Baekjoon vs Programmers: Baekjoon-style judges give you main() and raw stdin/stdout, so the setup above applies. Programmers and LeetCode have you implement a function (int solution(vector<int> v)) and handle I/O themselves, so fast-I/O lines do nothing there, but hidden copies still matter: take large parameters by const& in your own helper functions.

Related Post: C++ Coding Test I/O Cheatsheet

Edge Case Check

What interviewers frequently check:

  • Empty input (N=0, empty array)
  • Min/max values (N=1, N=10⁶)
  • Duplicate elements (all elements same)
  • Negative/zero (did problem assume positive only?)
  • Overflow (exceeds int range → use long long)

Example:

// ❌ Crashes when N=0
int maxVal = v[0];
for (int x : v) maxVal = std::max(maxVal, x);

// ✅ Handle empty array
if (v.empty()) return -1;  // Or handle exception
int maxVal = *std::max_element(v.begin(), v.end());

Code Readability

In interviews, “readable code” is as important as correct answer.

1. Meaningful variable names:

// ❌
int a = 0, b = 0;

// ✅
int leftPointer = 0, rightPointer = n - 1;

2. Function separation:

// ✅ Separate complex logic into functions
bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) return false;
    }
    return true;
}

3. Comments (when needed):

// Find pair with sum equal to target using two pointers
int left = 0, right = n - 1;

Interview Day Checklist

Problem Understanding Phase (5 minutes)

  • Check input format and range (N ≤ ?, negatives allowed?)
  • Check output format (multiple lines? space-separated?)
  • Trace example input/output by hand
  • Think about edge cases (N=0, N=1, duplicates, max value)

Algorithm Design Phase (5-10 minutes)

  • Calculate time complexity of brute force
  • Determine if possible within time limit
  • Think of optimization methods (sorting? hashmap? DP?)
  • Explain approach to interviewer (essential for whiteboard interviews)

Coding Phase (15-30 minutes)

  • Copy-paste I/O optimization template (sync_with_stdio, cin.tie)
  • Clear function/variable names
  • Check loop ranges (< vs <=, index 0 vs 1)
  • Check overflow (int vs long long)

Testing Phase (5-10 minutes)

  • Run with example input
  • Test edge cases directly (N=0, N=1, max value)
  • Recheck time complexity

Explanation Phase (to interviewer)

  • Explain algorithm approach
  • Mention time/space complexity
  • Mention optimization possibilities (if time permits)

Frequently Asked Interview Questions

”What is the time complexity?”

Example Answer:

“Sorting takes O(N log N), then two pointers takes O(N), so total is O(N log N)."

"What about space complexity?”

Example Answer:

“Besides input array, I use a hashmap, so worst case is O(N)."

"Can you optimize further?”

Example Answer:

“Currently O(N log N), but if input is limited to specific range, could reduce to O(N) with counting sort."

"How do you handle edge cases?”

Example Answer:

“When N=0, I return empty array, and when N=1, I return first element directly with exception handling.”


Practical Preparation Roadmap

Stage 1: Review Basic Data Structures/Algorithms (1-2 weeks)

  • Data Structures: Array, linked list, stack, queue, hashmap, tree, heap
  • Algorithms: Sorting, searching, recursion, DP, graph (BFS/DFS), greedy

Recommended Resources:

  • Baekjoon step-by-step problems
  • Programmers Level 1-2

Stage 2: Problem Solving by Type (2-3 weeks)

  • Sort/Search: Baekjoon 10 problems
  • DP: Baekjoon 10 problems (Fibonacci, LIS, knapsack)
  • Graph: BFS/DFS 5 problems each
  • Greedy: 5 problems
  • Two Pointers/Sliding Window: 5 problems

Stage 3: Mock Interview (1 week)

  • Set 30-minute timer and solve problems
  • Write on whiteboard by hand (without typing)
  • Mock interview with friend/colleague (practice explaining)

Stage 4: Past Exam Problems

  • Search for coding test reviews of target companies (Baekjoon, Programmers, etc.)
  • Intensive practice on similar types

Communication Tips During Interview

Confirm Understanding of Problem

“I want to confirm my understanding. The input is an array of N integers, and I need to return the indices of two elements that sum to K, correct?”

Explain Approach First

“First I’ll sort the array, then use two pointers from both ends to narrow down and check the sum. Time complexity is O(N log N).”

Request Hints When Stuck

“I’m currently thinking about how to define the DP table. Could you give me a hint?”

Mention Trade-offs

“Using hashmap is O(N) but space complexity is O(N), while sorting is O(N log N) but space is O(1). Which would be better?”


Common Mistakes: TLE, Wrong Answer, Memory Limit

These are the C++-specific ways a correct idea fails. Most of them pass the sample tests, which is what makes them expensive.

No I/O Optimization

Submitting heavy I/O problems without sync_with_stdio(false), cin.tie(nullptr) and '\n' → time limit exceeded with an otherwise optimal algorithm.

int Overflow

int a = 1000000, b = 1000000;
long long wrong = a * b;              // ❌ multiplied as int (10¹²), overflows before the assignment
long long right = (long long)a * b;   // ✅ promote one operand first

int holds about ±2.1×10⁹. The trap is that the declared type of the result does not matter: the multiplication happens in the type of the operands. The same applies to accumulate(..., 0), to mid = (lo + hi) / 2 when lo + hi can exceed the range (use lo + (hi - lo) / 2), and to sums of distances or counts of pairs, which grow as N². When in doubt, use long long for anything that is summed or multiplied.

Array Out of Bounds

int arr[100];
for (int i = 0; i <= 100; ++i) {  // ❌ i=100 is out of bounds
    arr[i] = 0;
}

Solution: Use i < 100, or v.at(i) while debugging. Out-of-bounds access is undefined behavior: sometimes it crashes (runtime error), but just as often it silently overwrites a neighboring variable and shows up as a wrong answer, which is far harder to trace.

Binary Search Without Sorting

binary_search, lower_bound and upper_bound only work on sorted ranges. On unsorted input they return meaningless results without any error.

std::vector<int> v = {3, 1, 4};
bool found = std::binary_search(v.begin(), v.end(), 3);  // ❌ Not sorted
std::sort(v.begin(), v.end());
found = std::binary_search(v.begin(), v.end(), 3);       // ✅

Also note that std::lower_bound(s.begin(), s.end(), x) on a std::set is O(N), because set iterators are not random access. Use the member function s.lower_bound(x), which is O(log N).

Uninitialized or Stale State

int main() {
    int dp[100][100];                  // ❌ local array: indeterminate values
    int dp2[100][100] = {};            // ✅ zero-initialized
    std::memset(dp, -1, sizeof(dp));   // ✅ every int becomes -1
    // std::memset(dp, 1, sizeof(dp)); // ❌ every int becomes 0x01010101 = 16843009, not 1
}

Local arrays hold garbage; global and static arrays are zero-initialized. memset fills bytes, so it only produces the intended int value for 0 and -1 (all bits zero or all bits one). For any other fill value, use std::fill or a vector constructor. For multiple test cases in one input, reset every array, visited flag and counter between cases. Stale state from the previous case is one of the most common reasons a solution passes the samples (often a single case) and fails the hidden tests.

Large Arrays on the Stack and Memory Limits

int grid[2000][2000];   // ✅ global: 16 MB in static storage

int main() {
    // int grid[2000][2000];  // ❌ 16 MB local array: likely stack overflow (runtime error)
}

The default stack is small (often 1–8 MB depending on the judge’s OS and settings), so large arrays belong at global scope or in a vector. Separately, estimate memory against the limit before coding: int arr[1000000][1000] is 4×10⁹ bytes, about 4 GB, far beyond a typical 128–512 MB limit. long long doubles every estimate, and std::bitset or std::vector<bool> store one bit per flag instead of one byte, which is often what makes a visited array for 10⁸ states fit.

Debug Output Left in the Submission

#ifdef LOCAL
#define debug(x) std::cerr << #x << " = " << (x) << '\n'
#else
#define debug(x)
#endif

debug(n);  // prints "n = 10" locally, compiles to nothing on the judge

Compile locally with g++ -DLOCAL ... and the macro disappears on the judge without editing the file. Writing to cerr instead of cout also means a forgotten debug line does not corrupt the expected output, although it still costs time, so don’t leave debug prints in hot loops.


PlatformFeaturesRecommended For
Baekjoon (BOJ)Korean, step-by-step problems, various difficultyKorean job seekers
ProgrammersKorean, company past problemsKorean company interview prep
LeetCodeEnglish, global company problems (FAANG)International jobs, high difficulty
CodeforcesEnglish, competitive programming styleAlgorithm skill improvement

Summary: For C++ coding tests, learn STL, I/O optimization, and frequently used patterns, then practice on Baekjoon/Programmers.


Frequently Asked Questions (FAQ)

Q. Can I always use bits/stdc++.h?

A. It is an internal header of GCC’s standard library (libstdc++). Most online judges compile with GCC, so it works there, but it does not exist on MSVC or on Clang with libc++ (the default toolchain on macOS), and it slows compilation because it pulls in the entire library. Use it in contests; in production code, and in interviews on an IDE you don’t control, include the specific headers:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
#include <map>
#include <set>
#include <queue>

Q. Is using namespace std; acceptable?

A. In contest code, yes: it saves typing and nobody else will include your file. In production headers it is a real problem, because it injects every standard name into every file that includes yours. Even in contests, naming a global variable count or distance next to using namespace std; can produce a confusing “reference is ambiguous” compile error, because those are also standard algorithm names.

Q. My solution passes the examples but fails hidden tests. What should I check first?

A. Integer overflow is the most common cause in C++: sums, products or counts that exceed about 2.1 billion overflow int, so use long long for them, including intermediate results like a * b where both operands are int and the initial value of accumulate. Next, check edge cases such as empty input, n = 1, duplicate values and the maximum constraints. For problems with multiple test cases, also make sure arrays, visited flags and counters are reset between cases.

Q. Why is my C++ solution getting TLE when the complexity looks right?

A. Look for hidden linear work: endl inside an output loop, missing sync_with_stdio(false), containers or strings passed by value into recursive functions, s = s + c string building, or repeated v.erase(v.begin()). Each one hides an O(N) cost inside a step you counted as O(1). If none apply, re-check the constraint math with the table in section 3.


For deeper follow-ups, the I/O cheatsheet covers the fast-I/O setup and its variants in more depth, and 10 classic coding test problems solved in C++ works through full solutions for the types above.