The ultimate quick-reference for recognizing problem patterns, choosing algorithms, and handling edge cases. Use this before every coding interview to prime your intuition and avoid common mistakes.
Read the problem statement. Find the signal below. Match the algorithm. Solve.
| Problem Signal | Core Algorithm | Time/Space | Example |
|---|---|---|---|
| Sorted array, find target | Binary Search | O(log n) / O(1) | LeetCode 704 (Search Insert Position) |
| Two pointers, find pair sum | Two Pointers | O(n) / O(1) | LeetCode 167 (Two Sum II) |
| "All permutations" or "generate all" | Backtracking + DFS | O(n!) / O(n) | LeetCode 46 (Permutations) |
| Min/max optimization on choices | Dynamic Programming or Greedy | O(n²) or O(n) / O(n) | LeetCode 121 (Best Time Buy Stock) |
| Shortest path (unweighted graph) | BFS | O(V+E) / O(V) | LeetCode 1091 (Shortest Path in Grid) |
| Shortest path (weighted graph) | Dijkstra | O((V+E) log V) / O(V) | LeetCode 743 (Network Delay Time) |
| Connected components, count groups | DFS / Union-Find | O(V+E) / O(V) | LeetCode 200 (Number of Islands) |
| Top K elements, k-th largest | Heap / QuickSelect | O(n log k) or O(n) avg / O(k) | LeetCode 215 (Kth Largest Element) |
| Count frequency, find duplicate | HashMap / HashSet | O(n) / O(n) | LeetCode 1 (Two Sum) |
| Substring with condition (length, chars) | Sliding Window | O(n) / O(1) or O(26) | LeetCode 3 (Longest Substring) |
| Tree traversal, search | DFS recursion or BFS | O(n) / O(h) recursive stack | LeetCode 104 (Max Depth) |
| Cycle detection in graph | DFS coloring or Topological Sort | O(V+E) / O(V) | LeetCode 207 (Course Schedule) |
| Overlapping intervals, merge | Sort + Sweep Line | O(n log n) / O(n) | LeetCode 56 (Merge Intervals) |
| Palindrome check or build | Two Pointers or Dynamic Programming | O(n) two-ptr, O(n²) DP / O(n) | LeetCode 5 (Longest Palindromic Substring) |
| Matrix/grid traversal, find path | BFS/DFS + visited set | O(m*n) / O(m*n) | LeetCode 79 (Word Search) |
| Linked list cycle, reverse | Fast/slow pointers | O(n) / O(1) | LeetCode 141 (Linked List Cycle) |
| Distinct subsequences, combinations | Dynamic Programming | O(n*m) / O(n*m) | LeetCode 1143 (Longest Common Subsequence) |
| Range sum queries | Prefix Sum or Segment Tree | O(1) query after O(n) / O(n) | LeetCode 303 (Range Sum Query) |
| Parentheses/brackets matching | Stack | O(n) / O(n) | LeetCode 20 (Valid Parentheses) |
| Rebuild tree from traversal | Recursion + HashMap for lookup | O(n) / O(n) | LeetCode 105 (Construct Binary Tree) |
| Median of data stream | Two Heaps (max-heap + min-heap) | O(log n) add / O(1) median | LeetCode 295 (Find Median) |
| LRU Cache design | HashMap + Doubly-Linked List | O(1) get/put / O(capacity) | LeetCode 146 (LRU Cache) |
| Integer overflow, bit manipulation | Bit operations or modular arithmetic | O(log n) / O(1) | LeetCode 371 (Sum of Two Integers) |
| Trie prefix matching, autocomplete | Trie (prefix tree) | O(m) search, O(m) insert / O(n*m) | LeetCode 208 (Implement Trie) |
| Topological sort, dependency order | Kahn's algorithm or DFS | O(V+E) / O(V) | LeetCode 207 (Course Schedule) |
| K-way merge, merge K sorted lists | Min-Heap / PriorityQueue | O(n log k) / O(k) | LeetCode 23 (Merge K Sorted Lists) |
| Rotate, shift array elements | Array reversal trick | O(n) / O(1) | LeetCode 189 (Rotate Array) |
| Partition, quicksort-style split | Two Pointers | O(n) / O(1) | LeetCode 75 (Sort Colors) |
| Trapping water, rain water | Two Pointers or Dynamic Programming | O(n) / O(n) | LeetCode 42 (Trapping Rain Water) |
| "Number of ways", count distinct | Dynamic Programming + HashMap | O(n) to O(n³) / O(n) | LeetCode 70 (Climbing Stairs) |
| Backtracking with constraints | DFS + memoization | O(n!) worst / O(n) | LeetCode 37 (Sudoku Solver) |
| Divide and conquer recursion | Merge Sort, Quick Sort, Binary Tree ops | O(n log n) / O(log n) stack | LeetCode 315 (Count Smaller) |
| Monotonic stack, next greater/smaller | Stack maintaining decreasing/increasing order | O(n) / O(n) | LeetCode 739 (Daily Temperatures) |
| Matrix chain multiplication order | Dynamic Programming + optimization | O(n³) / O(n²) | LeetCode 1039 (Minimum Score) |
| Edit distance, string similarity | Dynamic Programming 2D | O(m*n) / O(m*n) | LeetCode 72 (Edit Distance) |
Given constraint n, which techniques are feasible? This lookup helps you eliminate impossible approaches.
| Input Size (n) | Feasible Complexity | Algorithm Class | Notes |
|---|---|---|---|
| n ≤ 10 | O(n!), O(2^n) | Brute force, permutations, subsets | Try all combinations. Backtracking acceptable. |
| n ≤ 20 | O(2^n), O(n³) | Bitmask DP, subset enumeration | 2^20 ≈ 1M. Avoid exponential beyond this. |
| n ≤ 100 | O(n³), O(n² log n) | DP, Floyd-Warshall, all-pairs | N³ ≈ 1M operations. Feasible in 1s. |
| n ≤ 500 | O(n³), O(n² log n) | DP, graph algorithms | 500³ ≈ 125M. Tight but usually OK. |
| n ≤ 10^4 | O(n²), O(n² log n) | Nested loops, sorting+analysis | 10K² = 100M. Must be efficient constant factors. |
| n ≤ 10^5 | O(n log n), O(n√n) | Sorting, binary search, segment trees | 100K log 100K ≈ 1.7M. Standard online judge limit. |
| n ≤ 10^6 | O(n), O(n log n) | Linear pass, hashmap, sorting | 1M operations is baseline. O(n) or O(n log n) only. |
| n ≤ 10^7 | O(n) | Single pass, prefix sums | 10M operations. No nested loops. |
| n ≤ 10^8 | O(n), optimized | Cache-friendly, minimal branching | 100M. Optimization matters (SIMD, cache locality). |
| n > 10^8 | O(log n), O(1) | Binary search, math formula, lookup | Only mathematical solutions. No iteration. |
Use this to decide an approach when seeing constraint n:
Before submitting, check EVERY edge case. These catch 80% of WA (Wrong Answer) verdicts.
long for sums.get()?abs(a - b) < 1e-9.a / b when b could be 0. Check first.i % n.Keep these templates in a text editor. Paste them when you recognize the pattern.
// Binary Search Template - Find target in sorted array public int BinarySearch(int[] arr, int target) { int left = 0, right = arr.Length - 1; while (left <= right) { int mid = left + (right - left) / 2; // Avoid overflow if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; // Search right } else { right = mid - 1; // Search left } } return -1; // Not found } // Variant: Find insertion position (leftmost) public int SearchInsert(int[] arr, int target) { int left = 0, right = arr.Length; while (left < right) { int mid = left + (right - left) / 2; if (arr[mid] < target) { left = mid + 1; } else { right = mid; } } return left; // Insert position }
// BFS Template - Shortest path in unweighted graph public int BFS(int[][] graph, int start, int end) { Queue<int> queue = new Queue<int>(); HashSet<int> visited = new HashSet<int>(); Dictionary<int, int> distance = new Dictionary<int, int>(); queue.Enqueue(start); visited.Add(start); distance[start] = 0; while (queue.Count > 0) { int node = queue.Dequeue(); if (node == end) { return distance[end]; } foreach (int neighbor in graph[node]) { if (!visited.Contains(neighbor)) { visited.Add(neighbor); distance[neighbor] = distance[node] + 1; queue.Enqueue(neighbor); } } } return -1; // No path found }
// DFS Template - Recursive traversal HashSet<int> visited = new HashSet<int>(); public void DFS(int node, int[][] graph) { if (visited.Contains(node)) { return; } visited.Add(node); // Process current node foreach (int neighbor in graph[node]) { if (!visited.Contains(neighbor)) { DFS(neighbor, graph); } } } // Iterative DFS using stack public void DFSIterative(int start, int[][] graph) { Stack<int> stack = new Stack<int>(); stack.Push(start); visited.Add(start); while (stack.Count > 0) { int node = stack.Pop(); // Process node foreach (int neighbor in graph[node]) { if (!visited.Contains(neighbor)) { visited.Add(neighbor); stack.Push(neighbor); } } } }
// Sliding Window Template - Fixed/variable window // Problem: Find longest substring with at most k distinct chars public int MaxWindowLength(string s, int k) { Dictionary<char, int> charCount = new Dictionary<char, int>(); int left = 0, maxLen = 0; for (int right = 0; right < s.Length; right++) { // Expand window: add s[right] if (!charCount.ContainsKey(s[right])) { charCount[s[right]] = 0; } charCount[s[right]]++; // Shrink window: remove from left while invalid while (charCount.Count > k) { charCount[s[left]]--; if (charCount[s[left]] == 0) { charCount.Remove(s[left]); } left++; } // Update answer maxLen = Math.Max(maxLen, right - left + 1); } return maxLen; }
// Two Pointers Template - Find pair with target sum public int[] TwoSum(int[] sorted, int target) { int left = 0, right = sorted.Length - 1; while (left < right) { int sum = sorted[left] + sorted[right]; if (sum == target) { return new int[] { left, right }; } else if (sum < target) { left++; // Need larger sum } else { right--; // Need smaller sum } } return new int[] { -1, -1 }; } // Variant: Partition array (e.g., sort colors 0,1,2) public void Partition(int[] arr, int k) { int left = 0, right = arr.Length - 1; while (left < right) { while (left < right && arr[left] < k) left++; while (left < right && arr[right] >= k) right--; // Swap int temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; } }
// Union-Find with path compression and union by rank public class UnionFind { private int[] parent; private int[] rank; public UnionFind(int n) { parent = new int[n]; rank = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; rank[i] = 0; } } // Find root with path compression public int Find(int x) { if (parent[x] != x) { parent[x] = Find(parent[x]); // Path compression } return parent[x]; } // Union by rank public bool Union(int x, int y) { int rootX = Find(x); int rootY = Find(y); if (rootX == rootY) return false; // Already connected if (rank[rootX] < rank[rootY]) { parent[rootX] = rootY; } else if (rank[rootX] > rank[rootY]) { parent[rootY] = rootX; } else { parent[rootY] = rootX; rank[rootX]++; } return true; // Successfully merged } }
// Prefix Sum Template - O(1) range queries public class RangeSumQuery { private long[] prefix; public RangeSumQuery(int[] nums) { prefix = new long[nums.Length + 1]; // Build prefix sum: prefix[i] = sum of nums[0..i-1] for (int i = 0; i < nums.Length; i++) { prefix[i + 1] = prefix[i] + nums[i]; } } // Query sum of nums[left..right] in O(1) public long RangeSum(int left, int right) { return prefix[right + 1] - prefix[left]; } } // For 2D matrix: prefix[i][j] = sum of rect from (0,0) to (i-1,j-1) public long RangeSum2D(long[][] prefix, int r1, int c1, int r2, int c2) { return prefix[r2+1][c2+1] - prefix[r1][c2+1] - prefix[r2+1][c1] + prefix[r1][c1]; }
// Monotonic Stack - Find next greater element public int[] NextGreaterElement(int[] nums) { Stack<int> stack = new Stack<int>(); Dictionary<int, int> result = new Dictionary<int, int>(); // Process in reverse to build monotonic decreasing stack for (int i = nums.Length - 1; i >= 0; i--) { // Pop smaller elements while (stack.Count > 0 && stack.Peek() < nums[i]) { stack.Pop(); } // Top of stack is next greater (or -1 if empty) result[nums[i]] = stack.Count > 0 ? stack.Peek() : -1; // Push current element stack.Push(nums[i]); } int[] answer = new int[nums.Length]; for (int i = 0; i < nums.Length; i++) { answer[i] = result[nums[i]]; } return answer; }
// Trie Template - Efficient prefix matching public class TrieNode { public Dictionary<char, TrieNode> Children { get; set; } public bool IsEndOfWord { get; set; } public TrieNode() { Children = new Dictionary<char, TrieNode>(); IsEndOfWord = false; } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } // Insert word - O(m) where m = word length public void Insert(string word) { TrieNode node = root; foreach (char ch in word) { if (!node.Children.ContainsKey(ch)) { node.Children[ch] = new TrieNode(); } node = node.Children[ch]; } node.IsEndOfWord = true; } // Search for exact word public bool Search(string word) { TrieNode node = FindNode(word); return node != null && node.IsEndOfWord; } // Search with prefix (no exact match needed) public bool StartsWith(string prefix) { return FindNode(prefix) != null; } private TrieNode FindNode(string word) { TrieNode node = root; foreach (char ch in word) { if (!node.Children.ContainsKey(ch)) { return null; } node = node.Children[ch]; } return node; } }
// Dijkstra - Shortest path in weighted graph public long[] Dijkstra(int n, List<(int neighbor, long weight)>[] graph, int start) { long[] dist = new long[n]; for (int i = 0; i < n; i++) dist[i] = long.MaxValue; dist[start] = 0; PriorityQueue<(long dist, int node)> pq = new(); pq.Enqueue((0, start), 0); while (pq.Count > 0) { (long d, int u) = pq.Dequeue(); if (d > dist[u]) continue; // Skip outdated entry foreach ((int v, long w) in graph[u]) { if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; pq.Enqueue((dist[v], v), (int)dist[v]); } } } return dist; }
Review this list before you start coding. Takes 5 minutes. Could save 50+ points.
Solve 100+ problems and patterns become automatic. When you see "sorted array + target", binary search pops into your head instantly. When you see "shortest path unweighted", BFS is your default. This is pattern recognition. The more you practice, the faster you solve.
You have the templates. You know the 35 patterns. You've reviewed edge cases. You understand the complexity trade-offs. Now it's execution. Trust your preparation. Stay calm. Read carefully. Test thoroughly. You'll pass.