Interview Patterns
Two pointers, sliding window, fast/slow, merge intervals. Pattern recognition wins interviews.
Interview Patterns
Recognizing the right pattern is more important than memorizing solutions.
1. Two Pointers
When: Sorted array, find pair with target sum, palindrome check, container with most water.
def two_sum_sorted(arr, target):
left, right = 0, len(arr) - 1
while left < right:
s = arr[left] + arr[right]
if s == target: return [left, right]
elif s < target: left += 1
else: right -= 1
return []
Time: O(n), Space: O(1).
2. Sliding Window
When: Subarray/substring with constraint (max sum, longest with k distinct chars, etc.)
def longest_substring_k_distinct(s, k):
freq = {}
left = best = 0
for right, c in enumerate(s):
freq[c] = freq.get(c, 0) + 1
while len(freq) > k:
freq[s[left]] -= 1
if freq[s[left]] == 0:
del freq[s[left]]
left += 1
best = max(best, right - left + 1)
return best
3. Fast & Slow Pointers
When: Cycle detection, find middle of linked list, nth from end.
def find_middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
4. Merge Intervals
When: Overlapping intervals, meeting rooms, merge calendar events.
def merge_intervals(intervals):
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
5. Binary Search on Answer
When: "Find minimum X such that condition holds" — answer space is monotonic (False...True or True...False).
def min_days_to_bloom(bloomDay, m, k):
def can_bloom(day):
blooms = flowers = 0
for d in bloomDay:
if d <= day:
flowers += 1
if flowers == k:
blooms += 1; flowers = 0
else:
flowers = 0
return blooms >= m
lo, hi = min(bloomDay), max(bloomDay)
while lo < hi:
mid = (lo + hi) // 2
if can_bloom(mid): hi = mid
else: lo = mid + 1
return lo
6. Monotonic Stack
When: Next greater element, largest rectangle in histogram, daily temperatures.
def next_greater(nums):
result = [-1] * len(nums)
stack = [] # indices of elements waiting for their "next greater"
for i, n in enumerate(nums):
while stack and nums[stack[-1]] < n:
result[stack.pop()] = n # n is the answer for stack[-1]
stack.append(i)
return result
7. Greedy Algorithms
Make the locally optimal choice at each step — works when problems have greedy choice property + optimal substructure.
# Activity selection: maximize non-overlapping intervals
def max_activities(intervals):
# Sort by end time — earliest finish first
intervals.sort(key=lambda x: x[1])
count = 0
last_end = float('-inf')
for start, end in intervals:
if start >= last_end:
count += 1
last_end = end
return count
Other greedy problems: fractional knapsack, Huffman coding, minimum spanning tree (Prim's/Kruskal's), jump game.
8. Backtracking
When: Generate all solutions, combinations, permutations, N-Queens, Sudoku.
def backtrack(candidate, result):
if is_complete(candidate):
result.append(candidate[:])
return
for choice in generate_choices(candidate):
if is_valid(choice):
candidate.append(choice) # make move
backtrack(candidate, result)
candidate.pop() # undo (backtrack)
9. Divide and Conquer
Break into sub-problems, solve recursively, combine. Classic examples: merge sort, quick sort, binary search, closest pair of points.
Iterative vs Recursive
| | Recursive | Iterative | |--|--|--| | Code clarity | Often cleaner | More boilerplate | | Stack overflow | Risk on deep/skewed input | Explicit stack, safe | | Preferred for | Trees (natural structure) | Large graphs, skewed trees |
Edge Cases Checklist
Before submitting any solution, verify these:
- Empty input (null, empty string, empty array)
- Single element
- All same elements
- Already sorted / reverse sorted
- Cycles in graphs or linked lists
- Disconnected graphs
- Skewed trees (worst case for BST ops)
- Integer overflow (less relevant in Python)
Complexity Quick Reference
| Complexity | Name | Example | |-----------|------|---------| | O(1) | Constant | Hash map lookup | | O(log n) | Logarithmic | Binary search, heap ops | | O(n) | Linear | Single pass | | O(n log n)| Linearithmic | Sorting, divide & conquer | | O(n²) | Quadratic | Nested loops | | O(2ⁿ) | Exponential | Recursion without memo | | O(n!) | Factorial | Permutations |
Pattern Recognition Cheat Sheet
| Signal in problem | Try | |-------------------|-----| | Sorted array, pair sum | Two pointers | | Subarray/substring constraint | Sliding window | | Linked list cycle | Fast & slow | | Overlapping intervals | Sort + merge | | Min/max feasibility | Binary search on answer | | Next greater/smaller | Monotonic stack | | Make locally optimal choices | Greedy | | All combinations/paths | DFS + backtracking | | Dependencies/ordering | Topological sort | | Shortest path (unweighted) | BFS | | Shortest path (weighted) | Dijkstra | | Connected components / cycles | Union-Find | | Repeated sub-problems | DP (top-down or bottom-up) |