Common Coding Interview Patterns
Master the essential patterns—sliding window, two pointers, fast-slow pointers—that solve 80% of linked list and array problems.
Common Coding Interview Patterns
Most coding interview problems fall into recognizable patterns. Once you internalize these patterns, you’ll see them everywhere and know immediately which approach fits. These patterns show up everywhere once you know to look for them: sliding window for subarray/substring problems, two pointers for pair problems in sorted arrays, fast-slow pointers for cycle detection, and merge intervals for overlapping ranges. Knowing these five patterns handles most easy and medium difficulty problems.
The interviewer’s goal isn’t to stump you—it’s to see how you think. Naming the pattern you recognize shows experience, and explaining why you’re choosing an approach demonstrates reasoning. Don’t jump straight to coding; spend 30 seconds outlining your approach first.
Introduction
Pattern 1: Sliding Window
When: Subarray/substring problems asking for max, min, or average.
def max_subarray_sum_k(arr, k):
"""Find maximum sum of subarray with exactly k elements."""
window_sum = sum(arr[:k])
max_sum = window_sum
for i in range(k, len(arr)):
window_sum += arr[i] - arr[i - k]
max_sum = max(max_sum, window_sum)
return max_sum
Variations:
- Fixed size k: expand by 1, shrink by 1
- Variable size: expand freely, contract when constraint violated
Pattern 2: Two Pointers (Opposite Ends)
When: Pair problems in sorted arrays, palindrome checks.
def pair_sum_sorted(arr, target):
"""Find pair with target sum in sorted array."""
left, right = 0, len(arr) - 1
while left < right:
current = arr[left] + arr[right]
if current == target:
return [left, right]
elif current < target:
left += 1
else:
right -= 1
return [-1, -1]
Key: Move left rightward to increase sum, right leftward to decrease.
Pattern 3: Fast-Slow Pointers
When: Cycle detection, finding middle, linked list problems.
def has_cycle(head):
"""Detect cycle in linked list using Floyd's algorithm."""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
def find_middle(head):
"""Find middle node of linked list."""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
Pattern 4: Merge Intervals
When: Overlapping intervals, scheduling, resource allocation.
def merge_intervals(intervals):
"""Merge all overlapping intervals."""
if not intervals:
return []
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for start, end in intervals[1:]:
if start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
Pattern 5: BFS on Graphs
When: Shortest path in unweighted graph, level-order traversal.
from collections import deque
def shortest_path(graph, start, end):
"""BFS finds shortest path in unweighted graph."""
queue = deque([(start, [start])])
visited = {start}
while queue:
node, path = queue.popleft()
if node == end:
return path
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, path + [neighbor]))
return None
Quick Recap Checklist
- Sliding window: subarray sum/average with fixed/variable window
- Two pointers: sorted array pairs, palindrome (opposite ends)
- Fast-slow: cycle detection, middle element (same start, different speeds)
- Merge intervals: sort by start, merge overlaps
- BFS: shortest path in unweighted graph
Deep Dives
Why Sliding Window Works
The sliding window transforms O(n²) brute force into O(n) by reusing previous computations. When you calculate sum(arr[i-k+1:i+1]) from scratch each iteration, you repeat k-1 additions unnecessarily. The window slides by subtracting the outgoing element and adding the incoming one — a constant-time operation. This works because the window maintains contiguous elements with guaranteed O(1) shift.
Two Pointers on Sorted Data
Two pointers exploit sorted data’s ordering property. When left moves right, the sum increases monotonically; when right moves left, the sum decreases. This monotonicity guarantees you never miss a valid pair — you explore exactly n combinations, not n². Uniqueness of pairs requires additional visited tracking.
Floyd’s Algorithm Convergence Proof
For cycle detection, if the cycle length is λ and the hare moves 2x fast while the tortoise moves 1x slow, they meet after at most λ steps inside the cycle. Proof: In λ steps, the hare completes exactly 2 full laps (2λ steps) while the tortoise completes 1 lap (λ steps). Since they start λ steps apart on a λ-cycle, they must meet. The meeting point is guaranteed before the hare laps the tortoise.
Real-world Failure Scenarios
| Scenario | What Went Wrong | Lesson |
|---|---|---|
| Amazon OA: Maximum subarray sum | Tried to use divide-and-conquer when sliding window was simpler | Match pattern to problem structure first |
| Google Phone Interview: Palindrome check | Used string reverse instead of two pointers | Avoided O(n) extra space when O(1) was possible |
| Meta Onsite: Course schedule problem | Forgot to track visited nodes in BFS | Always track visited/discovered nodes in graphs |
| Stripe Interview: Merge overlapping meetings | Skipped sorting step due to time pressure | Sort first — it’s often half the algorithm |
| Netflix Interview: Longest substring with k distinct | Failed to shrink window when constraint violated | Variable-size windows need explicit contraction logic |
Trade-off Analysis
| Pattern | Time | Space | Best For | Limitation |
|---|---|---|---|---|
| Sliding Window (fixed) | O(n) | O(1) | Subarray sums, averages | Only works when window size is predetermined |
| Sliding Window (variable) | O(n) | O(1) | Longest substring with constraint | Requires clear contraction condition |
| Two Pointers | O(n) | O(1) | Pair sums in sorted arrays | Input must be sorted; doesn’t find all pairs in unsorted |
| Fast-Slow Pointers | O(n) | O(1) | Cycle detection, middle element | Only detects presence, not position |
| Merge Intervals | O(n log n) | O(n) | Scheduling, resource allocation | Requires sorting; O(n) space for output |
| BFS | O(V+E) | O(V) | Shortest path, level order | Doesn’t handle weighted edges; exponential space in worst case |
Interview Questions
Ask: Is the input sorted? Two pointers (opposite ends). Does it ask about subarrays/substrings? Sliding window. Is it a linked list? Fast-slow pointers. Are there intervals/ranges? Merge intervals. Is it shortest path in unweighted graph? BFS. The problem's structure usually hints at the pattern.
Some problems require combining patterns. "Longest substring with k distinct characters" uses sliding window. "Detect cycle in linked list" uses fast-slow. A problem like "Clone graph with random pointers" might need BFS + hash map for tracking visited nodes. Start with the dominant pattern and adapt as needed.
Expected answer points:
- BFS for shortest path in unweighted graphs — level-order guarantees minimum edges from start
- DFS for detecting cycles, topological sort, or when path existence is sufficient
- Memory consideration: BFS can explode with wide graphs; DFS with deep graphs
- Recursive DFS requires O(n) stack space; iterative BFS requires O(n) queue
Expected answer points:
- Reuses previous sum instead of recalculating from scratch each iteration
- Each element enters and exits the window exactly once — 2n operations total
- Constant-time update: subtract arr[i-k], add arr[i]
Expected answer points:
- Two pointers work correctly only on sorted input
- Unsorted arrays break the monotonicity guarantee — you may miss valid pairs
- Use hash-based approach for unsorted: O(n) time, O(n) space
Expected answer points:
- Floyd's algorithm: slow moves 1 step, fast moves 2 steps per iteration
- If cycle exists, fast laps slow inside the cycle — they must meet
- Meeting proves cycle existence; finding cycle start requires second phase
- No visited set needed — O(1) space via pointer collision
Expected answer points:
- Sort by start time — ensures we're processing intervals in order
- Maintain merged list where last interval's end is always the maximum end seen so far for overlapping intervals
- If next start <= last end, extend the last interval; otherwise, start new interval
Expected answer points:
- Sliding window: O(n) time, O(1) space — constant window size
- Two pointers: O(n) time, O(1) space — sorted array traversal
- Fast-slow pointers: O(n) time, O(1) space — cycle detection without visited set
- Merge intervals: O(n log n) time, O(n) space — sorting dominates
- BFS: O(V+E) time, O(V) space — queue stores all vertices at worst
Expected answer points:
- Check window expansion: verify arr[i] is added correctly
- Check window contraction: verify arr[i-k] is subtracted when window slides
- Verify constraint logic for variable-size windows — shrink condition must be correct
- Test edge cases: k=1, k=n, all same elements, empty input
Expected answer points:
- Standard two pointers skip duplicates automatically — left increments past equal elements
- For "all unique pairs" variant: add visited set to track seen values
- For "triplets that sum to target": skip duplicate values for each pointer position
- Key insight: sorting enables duplicate handling via position checks
Expected answer points:
- No — sorting by start time is essential to the O(n log n) bottleneck
- Without sorting, you'd need to check every pair — O(n²) worst case
- Space-time trade-off: sorting costs O(n log n) but enables single-pass O(n) merge
- Some interval problems like "insert interval" can use interval trees for O(log n) insertions
Expected answer points:
- Maximum queue size = maximum width of the graph at any level
- Occurs at the level closest to the start node with most vertices
- Worst case: star graph where center connects to all other vertices — O(V) queue
- Line graph has maximum size of 1 — only adjacent nodes enqueued
Expected answer points:
- 2x guarantees convergence — any faster speed also works but wastes moves
- 1x speed means both pointers move same pace and never meet
- Mathematical proof: if cycle length is λ, fast laps slow within λ steps at 2x speed
- 2 is the minimal integer speed that guarantees meeting while maximizing relative velocity
Expected answer points:
- Fix first pointer, use two pointers on remaining sorted subarray
- Skip duplicate values for the fixed pointer to avoid repeated triplets
- When sum == 0, record triplet and skip both left and right duplicates
- Time: O(n²) — sort O(n log n) plus n iterations each calling O(n) two-pointer scan
Expected answer points:
- Empty intervals list — return empty immediately
- Single interval — return it as-is
- Non-overlapping intervals — each becomes separate merged interval
- Fully contained intervals — keep the outer boundaries
- Intervals with same start value — merge by taking max end
Expected answer points:
- When you need to find ANY path (not shortest) — DFS explores depth first
- When memory is tight: BFS worst-case O(V) vs DFS O(V) but different distribution
- For topological sort — only DFS-based approach works reliably
- When solution is deep in the tree — BFS might explore many irrelevant nodes
Expected answer points:
- Fixed-size sliding window still works — it only tracks sum algebraically
- Variable-size window (max length subarray with sum < k) works — contraction triggers on constraint violation
- Maximum subarray sum (Kadane's algorithm) doesn't use sliding window — needs dynamic programming
- Key insight: negative numbers don't break the sliding mechanism — only the interpretation changes
Expected answer points:
- Detection: Floyd's algorithm with slow/fast pointers — O(1) space
- Finding start: after detection, reset one pointer to head and move both 1 step — they meet at cycle start
- Proof: distance from head to cycle start = distance from meeting point to cycle start
- Common follow-up: finding cycle length — count steps from start node until you return
Expected answer points:
- Empty graph (no vertices): return empty result, queue initializes empty
- Single vertex, no edges: return [start], visited set contains only that vertex
- Edge case in code: while queue: loop naturally handles empty — body never executes
- Always initialize visited with start node to prevent reprocessing
Expected answer points:
- Fast pointer reaches null (end) when no cycle exists — loop terminates returning false
- Fast.next or fast.next.next being null indicates end of list
- If cycle exists, fast pointer loops indefinitely and never reaches null
- Important: fast must check fast and fast.next simultaneously — order matters
Further Reading
- Grokking the Coding Interview — Visual practice with pattern-based problem solving
- LeetCode Patterns — Filter problems by pattern (array, string, linked list, etc.)
- Visualgo — Step-by-step algorithm visualization including sorting, BST, and graph traversal
- Timothy H. Chang - Graph Algorithms — BFS/DFS with Python implementation walkthrough
- Floyd’s Tortoise and Hare Explained — Mathematical proof behind fast-slow pointer convergence
Conclusion
With these five patterns, you can handle most easy and medium difficulty problems. When you encounter a problem, ask whether the input is sorted (two pointers), about subarrays or substrings (sliding window), a linked list (fast-slow), intervals (merge), or shortest path (BFS). Combining patterns is common—BFS plus a hash map for visited tracking, or sliding window with a hash map for character counting. Understanding why each pattern works is more valuable than memorizing solutions.
Category
Related Posts
Amazon/Google/Microsoft Tag Problems: Interview Patterns
Learn which data structures, algorithms, and problem patterns Amazon, Google, and Microsoft interviewers favor—and how to prepare for each company's style.
Code Quality and Edge Cases in Technical Interviews
Learn how to write clean, maintainable code that impresses interviewers by handling edge cases, naming variables well, and organizing solutions modularly.
Binary Search Variants: Beyond Simple Lookup
Master variations of binary search including lower bound, upper bound, search in rotated array, and fractional searching for optimization problems.