Software Engineer Hub

Algorithms and Data Structures for Software Engineers (2026)

In short

Algorithm interviews in 2026 reward pattern recognition, not problem volume. The candidates who clear FAANG-tier coding rounds have ~150 problems deep — not 500 shallow. This page walks the eight patterns that cover ~80% of senior+ coding interviews, with annotated Python code and the named LeetCode problems where each pattern is the canonical solution. Source structure follows NeetCode's 150 categorization (neetcode.io/roadmap) with worked examples that explain when each pattern is the right tool.

Key takeaways

  • ~80% of senior+ coding rounds map to 8 patterns: two pointers, sliding window, fast/slow pointers, BFS, DFS, backtracking, dynamic programming, and monotonic stack/deque (per NeetCode 150 categorization, neetcode.io/roadmap).
  • BFS finds shortest paths in unweighted graphs and explores level-by-level; DFS explores deeply and is correct for cycle detection, topological sort, and connectivity. Confusing them costs the round.
  • Sliding window collapses a nested-loop O(n^2) into O(n) when the inner loop only ever shrinks or grows monotonically — LeetCode 76 'Minimum Window Substring' is the canonical hard problem.
  • Dynamic programming is recognition (overlapping subproblems + optimal substructure), then formulation (state + transition), then implementation (memo or tabulation). Most candidates fail at recognition. LeetCode 198 'House Robber' is the simplest 1D DP teach-the-pattern problem.
  • Time complexity questions are graded on the worst case and the explanation, not the symbol — say 'O(V+E) because BFS visits each vertex once and each edge twice in adjacency list', not just 'O(V+E)'.
  • Mock interviews close the gap between solving alone and solving under pressure — Hello Interview's mock pool and interviewing.io give realistic feedback; aim for 8+ before applying to FAANG-tier.

Pattern 1: Two pointers (when both ends move toward each other)

Two pointers is the simplest non-trivial pattern and the highest-leverage one to internalize first. Recognition trigger: a sorted array (or one you can sort), and the answer involves comparing two elements.

Canonical problem: LeetCode 167 'Two Sum II — Input Array Is Sorted'.

def two_sum_sorted(nums: list[int], target: int) -> list[int]:
    """
    Return 1-indexed positions of two numbers summing to target.
    O(n) time, O(1) space — strictly better than the hashmap O(n) space approach.
    """
    left, right = 0, len(nums) - 1
    while left < right:
        s = nums[left] + nums[right]
        if s == target:
            return [left + 1, right + 1]
        elif s < target:
            left += 1   # need bigger sum; advance the small side
        else:
            right -= 1  # need smaller sum; retreat the big side
    return []  # never hit on valid input

When two pointers is wrong: when the array isn't sorted and the cost of sorting (O(n log n)) exceeds the cost of a hashmap solution (O(n) time, O(n) space). LeetCode 1 'Two Sum' on an unsorted array — use the hashmap, not two pointers.

Other LeetCode problems where two pointers is canonical:

  • 15 — 3Sum (sort first, then two pointers inside a loop, O(n^2)).
  • 11 — Container With Most Water (the trick: move the shorter pointer; the taller one can never improve from this side).
  • 42 — Trapping Rain Water (two pointers + tracking max-so-far on each side).
  • 125 — Valid Palindrome (two pointers on string with skip-non-alpha).

Pattern 2: Sliding window (variable-length subarray with a constraint)

Sliding window converts O(n^2) brute-force scans into O(n) by maintaining a window state and shrinking from one side as it expands from the other. Recognition trigger: 'longest/shortest/sum-of subarray such that...'

Canonical problem: LeetCode 3 'Longest Substring Without Repeating Characters'.

def longest_unique(s: str) -> int:
    seen = {}            # char -> last seen index
    left = 0
    best = 0
    for right, ch in enumerate(s):
        if ch in seen and seen[ch] >= left:
            # shrink window: jump left to past the duplicate
            left = seen[ch] + 1
        seen[ch] = right
        best = max(best, right - left + 1)
    return best
# Time: O(n) — each index is visited at most twice (once by right, once by left).
# Space: O(min(n, alphabet_size))

The hard problem candidates miss: LeetCode 76 'Minimum Window Substring'. Track required chars with a counter; expand right until window covers all; then shrink left while still valid; record minimum. The canonical pattern of 'expand to satisfy, shrink to optimize'.

from collections import Counter

def min_window(s: str, t: str) -> str:
    if not s or not t:
        return ""
    need = Counter(t)
    have = Counter()
    needed_chars = len(need)
    have_chars = 0
    left = 0
    best = (float('inf'), 0, 0)  # length, l, r
    for right, ch in enumerate(s):
        have[ch] += 1
        if ch in need and have[ch] == need[ch]:
            have_chars += 1
        while have_chars == needed_chars:
            if right - left + 1 < best[0]:
                best = (right - left + 1, left, right)
            have[s[left]] -= 1
            if s[left] in need and have[s[left]] < need[s[left]]:
                have_chars -= 1
            left += 1
    return s[best[1]:best[2] + 1] if best[0] < float('inf') else ""

Other LeetCode problems for the pattern: 209 (Minimum Size Subarray Sum), 424 (Longest Repeating Character Replacement), 567 (Permutation in String).

Pattern 3: BFS vs DFS — when to use which

This is the single most-failed concept in graph interviews. The wrong choice doesn't just cost time; it produces a wrong answer.

Use BFS when:

  • You need the shortest path in an unweighted graph.
  • You need to process nodes level by level (e.g., binary tree level order traversal — LeetCode 102).
  • You need to find the minimum number of steps (LeetCode 127 'Word Ladder').

Use DFS when:

  • You need to detect cycles in a directed graph (with three-coloring: unvisited, in-progress, finished).
  • You need topological sort (DAGs — LeetCode 207 'Course Schedule', 210 'Course Schedule II').
  • You need to explore all paths or generate combinations (backtracking is DFS).
  • You're doing connected component detection where order doesn't matter (LeetCode 200 'Number of Islands').

BFS template (use this verbatim):

from collections import deque

def bfs_shortest_path(graph: dict, start, target):
    """Return the shortest path length from start to target, or -1."""
    if start == target:
        return 0
    visited = {start}
    queue = deque([(start, 0)])  # (node, distance)
    while queue:
        node, dist = queue.popleft()
        for neighbor in graph[node]:
            if neighbor == target:
                return dist + 1
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append((neighbor, dist + 1))
    return -1
# Time: O(V + E). Space: O(V) for visited + queue.

DFS iterative template (avoid recursion depth limits in Python):

def dfs_iter(graph: dict, start) -> set:
    """Return all nodes reachable from start."""
    visited = set()
    stack = [start]
    while stack:
        node = stack.pop()
        if node in visited:
            continue
        visited.add(node)
        for neighbor in graph[node]:
            if neighbor not in visited:
                stack.append(neighbor)
    return visited

The annotated 'Number of Islands' walkthrough (LeetCode 200, the canonical interview problem):

def num_islands(grid: list[list[str]]) -> int:
    if not grid:
        return 0
    rows, cols = len(grid), len(grid[0])
    count = 0

    def dfs(r: int, c: int) -> None:
        # Out-of-bounds or water? stop.
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != '1':
            return
        # Mark this land cell visited (in-place mutation; ask the interviewer first).
        grid[r][c] = '#'
        dfs(r + 1, c); dfs(r - 1, c); dfs(r, c + 1); dfs(r, c - 1)

    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                count += 1
                dfs(r, c)
    return count
# Time: O(rows * cols). Space: O(rows * cols) recursion stack worst case.

For very large grids, switch to iterative DFS or BFS — Python's recursion limit (~1000) will blow on 1000x1000 grids.

Pattern 4: Dynamic programming — recognition, formulation, implementation

DP is where most candidates lose senior+ rounds. Not because the problems are hard but because the recognition step is non-obvious. The recognition signal is two-part: overlapping subproblems and optimal substructure.

Teach-the-pattern problem: LeetCode 198 'House Robber'.

You are robbing houses on a street. You can't rob two adjacent houses. Maximize total loot.

Formulation (the load-bearing step):

# State: dp[i] = max money robbing houses 0..i
# Transition: dp[i] = max(dp[i-1],         # skip house i
#                          dp[i-2] + nums[i]) # rob house i
# Base case: dp[0] = nums[0]; dp[1] = max(nums[0], nums[1])

Tabulation implementation (preferred over memoization in interviews — easier to reason about complexity):

def rob(nums: list[int]) -> int:
    if not nums:
        return 0
    if len(nums) == 1:
        return nums[0]
    # Space-optimized: only need the last two values.
    prev2 = nums[0]
    prev1 = max(nums[0], nums[1])
    for i in range(2, len(nums)):
        cur = max(prev1, prev2 + nums[i])
        prev2, prev1 = prev1, cur
    return prev1
# Time: O(n). Space: O(1) — note the optimization from O(n) to O(1).

Mentioning the space optimization from O(n) array to O(1) two-variable rolling state is a standard senior-bar signal.

Common DP problem catalog (LeetCode numbers):

  • 1D DP: 70 (Climbing Stairs), 198 (House Robber), 213 (House Robber II), 322 (Coin Change), 300 (Longest Increasing Subsequence).
  • 2D DP / strings: 5 (Longest Palindromic Substring), 1143 (Longest Common Subsequence), 72 (Edit Distance).
  • 2D DP / grids: 62 (Unique Paths), 64 (Minimum Path Sum), 221 (Maximal Square).
  • Knapsack family: 416 (Partition Equal Subset Sum), 494 (Target Sum), 518 (Coin Change II).

The single book that teaches DP correctly: Algorithms by Sedgewick and Wayne (4th ed., chapter 6). Cracking the Coding Interview's DP coverage is shallow; CLRS is more theoretical than interviews require.

Pattern 5: Backtracking, monotonic stack, and the rest

Three more patterns that round out senior+ coverage.

Backtracking (DFS with state restoration). Recognition: combinatorial enumeration ('find all permutations / subsets / valid combinations'). Template:

def backtrack(state, choices):
    if is_solution(state):
        result.append(list(state))  # copy!
        return
    for choice in valid_choices(state, choices):
        state.append(choice)
        backtrack(state, choices)
        state.pop()  # the load-bearing step — restore state

Canonical: LeetCode 46 (Permutations), 78 (Subsets), 39 (Combination Sum), 51 (N-Queens).

Monotonic stack/deque. Recognition: 'next greater element' or 'sliding window maximum'.

# LeetCode 739 — Daily Temperatures: how many days until a warmer one?
def daily_temperatures(temps: list[int]) -> list[int]:
    answer = [0] * len(temps)
    stack = []  # indices, temps decreasing
    for i, t in enumerate(temps):
        while stack and temps[stack[-1]] < t:
            j = stack.pop()
            answer[j] = i - j
        stack.append(i)
    return answer
# Time: O(n) — each index pushed and popped at most once.

Other monotonic-stack problems: 84 (Largest Rectangle in Histogram), 239 (Sliding Window Maximum — monotonic deque), 503 (Next Greater Element II).

Fast/slow pointers (Floyd's tortoise and hare). Recognition: cycle detection in linked lists, finding the middle.

# LeetCode 141 — Linked List Cycle
def has_cycle(head) -> bool:
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast:
            return True
    return False

Plus: LeetCode 142 (find cycle start — clever math), 876 (middle of linked list).

Time and space complexity — how to talk about it

Stating complexity is grade-school; justifying complexity is the senior bar. The wrong answer is the symbol; the right answer is one sentence of why.

AlgorithmTimeSpaceJustification (say this out loud)
BFS / DFS on graphO(V + E)O(V)Each vertex visited once; each edge inspected at most twice (adjacency list).
Sorting (Timsort, Python default)O(n log n)O(n)Worst case is the merge phase; auxiliary array needed.
Sliding windowO(n)O(window)Each index touched by at most two pointers (left, right).
DP, 2DO(rows * cols)O(rows * cols) or O(min)Each cell computed once; sometimes you can reduce to O(prev row only).
BacktrackingO(branching^depth)O(depth)State stack at most equal to depth; pruning reduces branching factor.
Heap / priority queue opsO(log n) per opO(n)Heap ops are log because of the bubble-up/down on a balanced binary tree.

Big mistakes candidates make:

  • Calling a Python list.append O(1) amortized but not saying 'amortized' — interviewers notice.
  • Calling a hashmap lookup O(1) without saying 'expected; worst case O(n) on hash collisions'.
  • Forgetting the recursion stack space in DFS — it's not free.
  • Confusing 'this runs fast in Python' with 'this is asymptotically optimal' — Python's hashmap is slower than C's by a factor; the asymptotics are the same.

Prep cadence: 12 weeks to senior-bar coding fluency

Empirical pattern from candidates who clear FAANG senior+ rounds in 2026:

  1. Weeks 1-2 — Pattern 1-2 (two pointers, sliding window): 15-20 LeetCode problems mediums; 5 hards. Don't move on until the recognition triggers are automatic.
  2. Weeks 3-4 — Trees and graphs (BFS, DFS): 25 problems. Trees first (LC 102, 103, 104, 226, 235, 236, 297), then graph (LC 200, 207, 210, 261, 547).
  3. Weeks 5-7 — Dynamic programming: 30 problems, this is the longest stretch. Start 1D (70, 198, 322), then 2D-string (1143, 72), then 2D-grid (62, 64), then knapsack (416, 494, 518).
  4. Weeks 8-9 — Backtracking, heap, monotonic stack: 20 problems.
  5. Weeks 10-12 — Mock interviews + targeted weak-area: 12+ mocks (Pramp, Hello Interview, interviewing.io). Mock interview reveals what passes-without-pressure but fails-with-pressure.

Don't: blind-grind LeetCode 'easy' problems. The easy tier maps to junior bar and won't move you to senior+. Mediums are the senior interview floor; ~80% of senior coding rounds at FAANG are 'medium-tagged' problems with one curveball.

The book if you read one: Algorithm Design Manual (Skiena), 3rd ed. Skiena's chapter 6 (sorting/searching), 7 (graph traversal), 8 (weighted graphs), and 10 (DP) cover what interviews test. Cracking the Coding Interview is fine for first-time prep but doesn't go deep enough for senior+. CLRS is theoretical and the wrong tool for an interview-prep budget.

The mock interview that closes the gap: interviewing.io's anonymous mocks with current-employee FAANG senior+ engineers. Three of those, with the same problem you'd face on-site, surface the gap between 'I can solve it' and 'I can solve it under pressure'.

Frequently asked questions

Is LeetCode 75 enough, or do I need NeetCode 150?
LeetCode 75 covers the patterns at a junior-to-mid bar. NeetCode 150 (neetcode.io/practice) extends the same patterns to the harder problems senior+ rounds draw from. If you can solve all of NeetCode 150 mediums in under 30 minutes each, you're at the senior FAANG bar. Volume past 150 has diminishing returns; depth on the patterns matters more.
Should I write the code in Python or C++?
Python unless the role is systems / performance-critical (where C++ or Rust is more credible). Python's syntax is fastest to write, and you spend coding rounds on logic, not syntax. Use type hints (PEP 484) — they signal seniority. The exception: at companies whose primary stack is Java or Go (Google, Stripe), use that language; mismatch reads as inflexibility.
How important is it to recognize the optimal solution immediately?
Less important than candidates think. Most senior interviewers prefer 'state the brute force first, get to a working solution, then optimize' over 'jumped to the optimal solution and missed an edge case'. Articulating the path from O(n^2) to O(n) is graded; landing the O(n) solution silently is not.
What if I freeze on a problem I haven't seen?
Slow down and verbalize. 'I haven't seen this exact problem; let me try a small example by hand' is what senior+ candidates say. Walk through inputs of size 2, 3, 4 by hand. Identify what changes between inputs. The pattern reveals itself in 60-90% of cases. Silent struggle reads worse than transparent reasoning, even when the silent candidate eventually solves it.
How does AI-augmented prep (Cursor + LeetCode) actually work?
Use it for two specific things: (1) explain a solution you already wrote and disagree with, where the disagreement reveals your gap; (2) generate 5 variations of a problem you just solved, to stress-test the pattern. Do not use it as a first-pass solver — the gap between 'AI solved it' and 'I solved it under pressure' is the entire grade.
Are bit manipulation and math problems still asked at FAANG in 2026?
Less than they used to be. The Apple and Bloomberg interviews still draw from bit manipulation (LC 191 'Number of 1 Bits', 338 'Counting Bits'). Most other FAANG-tier rounds have moved to graph and DP problems. Spend ~5% of prep time on bit manipulation; not zero, but not the focus.
How important is system-design fluency at the algorithms round?
Zero in the algorithms round itself. But: senior+ interviews often have a coding round where the solution opens a system-design discussion. If you finish coding in 20 minutes, the interviewer may ask 'now, how would this scale to 100M users?' — be ready to switch modes. The two skills are tested separately but adjacent.
Should I memorize Python's collections module?
Yes. <code>collections.deque</code> for O(1) appends/pops both ends, <code>collections.Counter</code> for frequency maps, <code>collections.defaultdict(list)</code> for adjacency lists, <code>heapq</code> for priority queues. Knowing these saves 5-10 minutes per problem. Python's standard library is the load-bearing layer of fast interview coding.

Sources

  1. NeetCode 150 roadmap — pattern-based categorization of senior+ interview problems.
  2. LeetCode — Top Interview 150 study plan.
  3. Skiena — The Algorithm Design Manual, 3rd edition (the book to read for senior+ prep).
  4. Sedgewick & Wayne — Algorithms, 4th ed. (Princeton). Best DP coverage in print.
  5. interviewing.io — anonymous mock interviews with current-employee FAANG senior+ engineers.
  6. Hello Interview — coding mock pool and senior-bar feedback.
  7. Cracking the Coding Interview (McDowell), 6th ed. — fine for first-pass prep, not senior-bar by itself.

About the author. Blake Crosley founded ResumeGeni and writes about product design, hiring technology, and ATS optimization. More writing at blakecrosley.com.