The Blind 75: learn 15 patterns, not 75 answers
Imagine trying to learn chess by memorizing every famous game move-for-move. You'd do terribly against any player who deviated from those exact games. The right way is to learn strategic patterns — pin attacks, forks, endgame techniques — and then recognize when to apply them. The Blind 75 works the same way.
🎯 The insight: The Blind 75 (a Facebook engineer's curated list) endures because it's the minimal set covering nearly every pattern interviews draw from. Used wrong, it's 75 memorized solutions that evaporate under pressure. Used right, it's ~15 patterns, each seen from 3–6 angles — this page groups it that way, with the one-line insight per problem.
Protocol per problem: attempt 25–30 min cold → if stuck, read only the insight here and try again → solved or not, say the approach out loud in one sentence → log it for re-attempt in a week. Marking "solved" after reading a solution is the cardinal sin — re-derive days later, or it didn't happen.
Quick reference — pattern to time complexity
| Pattern | Typical complexity | Key data structure |
|---|---|---|
| Hash map | O(n) time, O(n) space | Hash table |
| Two pointers | O(n) time, O(1) space | Sorted array |
| Sliding window | O(n) time, O(k) space | Window + hash/set |
| Stack | O(n) time, O(n) space | Stack |
| Binary search | O(log n) time, O(1) space | Sorted array |
| Linked list (fast/slow) | O(n) time, O(1) space | Pointers |
| Tree recursion | O(n) time, O(h) space | Recursion stack |
| BFS | O(V+E) time, O(V) space | Queue |
| Heap | O(n log k) time, O(k) space | Priority queue |
| Backtracking | O(2^n) worst | Recursion |
| DP (1-D) | O(n) time, O(n) space | Array |
| DP (2-D) | O(m·n) time, O(m·n) space | Matrix |
Arrays & Hashing — hash tables, arrays
| Problem | The insight |
|---|---|
| Two Sum | dict of seen values → complement lookup, one pass |
| Contains Duplicate | set membership |
| Product of Array Except Self | prefix products × suffix products, no division |
| Maximum Subarray | Kadane: running sum, reset when it goes negative (1-D DP in disguise) |
| Maximum Product Subarray | track running max and min (negatives flip them) |
| Top K Frequent | count dict → bucket-by-frequency (or heap) |
| Group Anagrams | canonical key: sorted word / letter counts |
| Valid Anagram | counting dict |
| Longest Consecutive Sequence | set + only start counting at left edges (x−1 absent) |
| Encode/Decode Strings | length-prefix framing — delimiters alone can't be safe |
# Two Sum — the hash map template
def two_sum(nums, target):
seen = {}
for i, x in enumerate(nums):
if target - x in seen:
return [seen[target - x], i]
seen[x] = i
return []
# Maximum Subarray — Kadane's algorithm
def max_subarray(nums):
curr_sum = max_sum = nums[0]
for n in nums[1:]:
curr_sum = max(n, curr_sum + n) # reset if negative prefix
max_sum = max(max_sum, curr_sum)
return max_sum
// Two Sum in Java
import java.util.HashMap;
public class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int partner = target - nums[i];
if (seen.containsKey(partner))
return new int[]{seen.get(partner), i};
seen.put(nums[i], i);
}
return new int[]{};
}
}
// Two Sum in C++
#include <unordered_map>
#include <vector>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> seen;
for (int i = 0; i < (int)nums.size(); i++) {
int partner = target - nums[i];
if (seen.count(partner)) return {seen[partner], i};
seen[nums[i]] = i;
}
return {};
}
Two Pointers — doc
| Problem | The insight |
|---|---|
| Valid Palindrome | opposite ends, skip non-alphanumerics |
| 3Sum | sort; fix one, two-pointer the rest; skip duplicates carefully |
| Container With Most Water | move the shorter wall inward — exchange argument |
| Trapping Rain Water | water[i] = min(maxL, maxR) − h[i]; two pointers carry the maxes |
Sliding Window — doc
| Problem | The insight |
|---|---|
| Best Time to Buy/Sell Stock | running min, best diff — window degenerates to one pass |
| Longest Substring Without Repeating | window + set; shrink from left past the repeat |
| Longest Repeating Character Replacement | window valid while (size − maxFreq) ≤ k |
| Minimum Window Substring | need/have counts; expand right, shrink left greedily |
Stack — doc
| Problem | The insight |
|---|---|
| Valid Parentheses | the canonical LIFO match |
Binary Search — doc
| Problem | The insight |
|---|---|
| Search Rotated Sorted Array | one half is always sorted — decide which, recurse into the right one |
| Find Min in Rotated Sorted Array | compare mid to right end; shrink toward the bend |
Linked List — doc
| Problem | The insight |
|---|---|
| Reverse Linked List | the three-pointer dance, from memory, both directions of asking |
| Merge Two Sorted Lists | dummy head + two pointers |
| Reorder List | middle → reverse second half → interleave (three sub-skills) |
| Remove Nth From End | lead pointer n ahead, then move both |
| Linked List Cycle | Floyd fast/slow |
| Merge K Sorted Lists | heap of k heads — O(N log k) |
Trees — doc
| Problem | The insight |
|---|---|
| Max Depth | the 3-line recursion template |
| Same Tree / Invert Tree | simultaneous recursion / swap-and-recurse |
| Subtree of Another Tree | same-tree check at every node |
| LCA of a BST | walk from root until the targets split |
| Validate BST | (low, high) bounds passed down — child-check fails |
| Level Order Traversal | BFS queue, level = current queue length |
| Kth Smallest in BST | in-order traversal, count down |
| Construct from Preorder + Inorder | preorder[0] is root; inorder splits children |
| Binary Tree Max Path Sum | post-order: best one-leg path up vs best arch through |
| Serialize/Deserialize | preorder with null markers |
# Tree max depth — the 3-line template every tree problem starts from
def max_depth(root):
if not root:
return 0
return 1 + max(max_depth(root.left), max_depth(root.right))
# Level order traversal — BFS queue
from collections import deque
def level_order(root):
if not root: return []
result, queue = [], deque([root])
while queue:
level = []
for _ in range(len(queue)): # freeze current level size
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(level)
return result
// Tree max depth — Java
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
// Tree max depth — C++
int maxDepth(TreeNode* root) {
if (!root) return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
Tries — doc
| Problem | The insight |
|---|---|
| Implement Trie | children dict + end-flag |
| Word Search II | trie over dictionary guides grid backtracking — prune dead prefixes |
| Design Add & Search Words | '.' wildcard → branch into all children (DFS) |
Heap — doc
| Problem | The insight |
|---|---|
| Find Median from Data Stream | two heaps: max-heap lower half, min-heap upper, rebalance |
Backtracking — doc
| Problem | The insight |
|---|---|
| Combination Sum | choose/explore/un-choose; reuse allowed → don't advance index |
| Word Search | grid DFS + mark/unmark visited |
Graphs — doc
| Problem | The insight |
|---|---|
| Number of Islands | flood fill (BFS/DFS) per unvisited land cell |
| Clone Graph | dict old→new while traversing |
| Pacific Atlantic Water Flow | reverse-flow BFS from both oceans; intersect |
| Course Schedule | cycle detection = topological sort (Kahn's / DFS colors) |
| Graph Valid Tree | connected + exactly n−1 edges (union-find) |
| Number of Connected Components | union-find counting, or DFS sweeps |
| Alien Dictionary | build precedence edges from adjacent words → topo sort |
Intervals — greedy + heaps
| Problem | The insight |
|---|---|
| Insert Interval | three phases: before, merge-overlaps, after |
| Merge Intervals | sort by start; extend or push |
| Non-overlapping Intervals | max meetings kept (sort by end) → n − kept |
| Meeting Rooms I / II | overlap check / heap of end times |
1-D DP — doc
| Problem | The insight |
|---|---|
| Climbing Stairs | Fibonacci — the hello-world recurrence |
| House Robber I / II | take[i] vs skip[i]; circular → run twice excluding one end |
| Coin Change | min coins per amount, bottom-up (the greedy-fails poster child) |
| Longest Increasing Subsequence | O(n²) DP, or patience trick with binary search O(n log n) |
| Word Break | dp[i] = any dp[j] && s[j:i] in dict |
| Decode Ways | climbing stairs with validity conditions |
| Palindromic Substrings / Longest Palindromic Substring | expand around 2n−1 centers |
2-D DP & Strings
| Problem | The insight |
|---|---|
| Unique Paths | grid[i][j] = top + left |
| Longest Common Subsequence | match → diag+1; else max(top, left) |
The remainder (bit/math/matrix — appear less, finish last)
Set/Clear bits family (Number of 1 Bits, Counting Bits, Missing Number, Reverse Bits, Sum of Two Integers), Rotate Image (transpose + reverse rows), Spiral Matrix (shrink four boundaries), Set Matrix Zeroes (first row/col as markers).
Do the groups top-to-bottom as listed — each reuses the previous (3Sum needs two pointers; trees need recursion comfort; graphs need BFS from trees; intervals need greedy). Two groups per week with re-attempts is a realistic 6-week pass; see the study plan for scheduling, then graduate to mock drills.
Think it through
PROBLEMTime yourself: for each of the three problems below, name the pattern and state the time/space complexity BEFORE revealing. (1) 'Climb to the top of a staircase with 1 or 2 steps at a time — how many distinct ways?' (2) 'Given n courses with prerequisites, determine if you can finish all courses.' (3) 'Find the length of the longest increasing subsequence in an array.'
- 1
Climbing Stairs
“It's asking to count 'distinct ways' to reach a target. Choices compound. What does this remind you of?”
- 2
Course Schedule
“Courses depend on other courses. What happens if course A requires B, which requires A?”
unlocks after the stage above - 3
Longest Increasing Subsequence
“You're looking for a subsequence — elements don't need to be contiguous. The word 'longest' + 'subsequence' is a classic DP signal. State the recurrence.”
unlocks after the stage above
Interview perspective
Practice — climb the ladder
Climb in order — every rung assumes the one above it. Solve on LeetCode, then tick it here; progress is saved on this device.
Must-do first
these 5 cover 3 patterns — your minimum viable set- Two SumEasy
Hash map complement lookup.
One-pass running min — window in disguise.
Stack — LIFO for nesting.
- Climbing StairsEasy
Fibonacci DP — entry 1-D DP.
- Maximum SubarrayMedium
Kadane's — reset when prefix goes negative.
Core group
cover the heavy-hitter patterns- Coin ChangeMedium
Bottom-up DP — the greedy-fails demo.
- Number of IslandsMedium
BFS/DFS flood fill — connected components.
Sliding window.
Three-pointer linked list template.
BFS on tree — queue + level size.
Stretch
compound and hard problemsMin-heap of k heads — O(N log k).
- Word Search IIHard
Trie pruning + backtracking grid search.
Two-heap balance trick.
Practice
The page is the practice. Track group-by-group; when all groups are green twice, you're ready for mock drills — the last step before the real thing.
Check yourself — 75 problems, 15 patterns
1. The Blind 75's core value is teaching you to:
2. You've read solutions to 50 Blind 75 problems and feel confident. But in a mock interview you blank. What happened?
3. 'Binary Tree Max Path Sum' — you can go left subtree only, right subtree only, or through root. Which traversal builds this?