Why a plan beats grinding
Most people prepare for coding interviews by opening a problem site and solving whatever shows up next. That feels productive but it is slow, because you keep relearning the same patterns and skipping the ones you find uncomfortable. A structured plan fixes the order, forces you to face your weak topics, and gives you a way to tell whether you are actually improving rather than just accumulating solved problems.
The difference is measurable. Someone grinding at random tends to solve a wide spread of easy problems, feel busy, and then freeze in the interview on a medium they have technically seen three variants of. Someone working a plan covers fewer problems but recognises the underlying pattern on sight, which is the skill the interview is actually testing. Interviewers are not checking whether you have memorised a catalogue. They are checking whether, faced with an unfamiliar prompt, you can map it to a structure you understand and reason out loud while you do it.
The plan below assumes you can already write a function and read your chosen language fluently. It runs for eight weeks at roughly one to two focused hours a day. If you have less time, stretch it out rather than cramming, because spaced practice sticks far better than a panicked sprint the week before the loop. If you have more time, do not add more problems per day. Add a second pass over the topics you found hardest. Depth beats breadth at almost every point in this process.
A useful test of progress: pick a problem you have never seen, read it once, and time how long it takes you to name the pattern and the rough complexity of the intended solution. At the start that might take ten minutes of flailing. By week eight it should take under a minute, even when you cannot yet code it. That recognition speed is what you are training.
Pick one language and one place to track work
Before week one, make two decisions. Choose a single language and stick with it. Switching languages mid-prep wastes effort on syntax instead of patterns. Most people use Python for the terse syntax and the rich standard library, but a language you already know well is usually the better pick, because under interview pressure you do not want to be fighting both the problem and the language.
Whatever you choose, learn its workhorse data structures cold: the dynamic array, the hash map, the hash set, a stack and queue, a heap or priority queue, and the sorting call. You should be able to write all of these from memory without pausing. In Python that means lists, dict, set, collections.deque, and heapq. In Java it means ArrayList, HashMap, HashSet, ArrayDeque, and PriorityQueue. Fumbling the container API in front of an interviewer reads as unfamiliarity even when your algorithm is correct.
Then set up a simple log. A spreadsheet with the problem, the pattern, whether you solved it unaided, the time it took, and a one line note on what tripped you up is enough. This log is what turns practice into a feedback loop. Without it you cannot see which patterns still need work, and you will drift back to the comfortable topics by default. The single most common reason a long prep produces a weak result is the absence of this honest record.
| Column | Why it matters |
|---|---|
| Problem and link | So you can redo it later without hunting |
| Pattern | The recall handle you are actually training |
| Solved unaided? | Separates real progress from peeking |
| Time taken | Reveals whether you are getting faster |
| What tripped me up | Turns each miss into a targeted fix |
Weeks one and two: arrays, strings, and hashing
Start with the foundations because they appear inside almost every harder problem. Cover arrays, strings, the two pointer technique, sliding window, and hash maps for lookups and counting.
The recognition skill matters more than the code. When you read "group these words so anagrams sit together," you want a hash map keyed by a canonical form of each word to come to mind immediately. When you read "are these two strings anagrams of each other," you want a frequency count rather than a sort. The phrasing of the prompt is a clue, and learning to read those clues is most of the battle.
def group_anagrams(words):
groups = {}
for w in words:
key = "".join(sorted(w)) # canonical form: sorted letters
groups.setdefault(key, []).append(w)
return list(groups.values())What good looks like here: you reach for the hash map keyed on a canonical form rather than comparing every pair of words, you state that this is O(n times k log k) for n words of length k (the sort per word dominates) and O(n times k) space, and you note the alternative of a 26-length count tuple as the key if the words are lowercase letters only, which drops the per-word cost to O(k).
What weak looks like: you compare each word against every other to test for an anagram, which is O(n squared times k), or you get the grouping right but cannot say why sorting the letters produces a stable key. Being able to explain the why is the part that transfers to harder problems.
Aim for around twenty problems across these two weeks. After each, write down the pattern, not just whether you got it right. By the end you should recognise two pointer and sliding window problems within the first minute of reading. Sliding window in particular has a reliable shape: a window that grows on the right, shrinks on the left when a condition breaks, and tracks a best result as it moves. Internalise that shape and a whole class of problems collapses into one template.
Weeks three and four: stacks, queues, linked lists, and trees
Move to the linear and hierarchical structures. Cover stacks for matching and parsing, queues, linked list manipulation, and binary tree traversals both recursive and iterative.
Linked lists reward a small set of techniques that recur constantly: the dummy head node to simplify edge cases, the fast and slow pointer for finding a midpoint or detecting a cycle, and in-place reversal. Once those three are automatic, most linked list questions become routine.
Trees are where breadth-first and depth-first search become essential. Practise both until the templates are automatic, because half of the harder questions reduce to a traversal. Depth-first comes in three orders (preorder, inorder, postorder) and it is worth knowing which order each problem wants. Inorder on a binary search tree, for instance, visits values in sorted order, which is the key to a surprising number of tree problems.
def level_order(root):
if not root:
return []
from collections import deque
out, queue = [], deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
out.append(level)
return outNote the use of deque rather than a list with pop(0). Popping from the front of a Python list is O(n) because every later element shifts down one slot, while deque.popleft() is O(1); this is documented in the Python time-complexity reference. That single wrong container quietly turns an O(n) traversal into O(n squared). Small choices like this are exactly what a sharp interviewer probes, so build the habit now.
Keep tracking which problems you needed a hint on. Those are the ones to revisit in week eight.
Weeks five and six: graphs and binary search
Graphs intimidate people, but most interview graph problems are just BFS or DFS on a structure you build from the input, such as a grid or an adjacency list. Cover connected components, shortest path in an unweighted graph, cycle detection, and topological sort. A grid is a graph in disguise: each cell is a node and its neighbours are the cells up, down, left, and right. Once you see that, "number of islands" and "rotting oranges" stop looking like separate problems and become the same traversal with a different bookkeeping detail.
The one habit that prevents most graph bugs is tracking visited nodes correctly. Forgetting to mark a node visited, or marking it at the wrong moment, causes infinite loops and double counting. Decide your convention (mark on enqueue for BFS, mark on visit for DFS) and apply it consistently.
Binary search deserves real attention because the hard version is not searching a sorted array. It is binary search on the answer: if you can write a check that says whether a candidate value is feasible, and feasibility is monotonic, you can search the space of answers rather than the input.
def min_capacity(weights, days):
def can_ship(cap):
used, current = 1, 0
for w in weights:
if current + w > cap:
used += 1
current = 0
current += w
return used <= days
lo, hi = max(weights), sum(weights)
while lo < hi:
mid = (lo + hi) // 2
if can_ship(mid):
hi = mid
else:
lo = mid + 1
return loThe shipping capacity example shows the pattern cleanly. There is no sorted array in the input. The insight is that larger capacities are always feasible if a smaller one is, so feasibility is monotonic, and you binary search the range of possible capacities. Spotting that monotonic structure in a word problem is the skill, and it only comes from doing several of these.
This is also the point to get comfortable reasoning about time and space complexity out loud. Interviewers expect you to state the complexity before you code, so practise saying it for every problem you solve. Get specific: "this is O(n log n) because of the sort, then a linear pass, so the sort dominates." Vague answers suggest you are reciting rather than reasoning.
Weeks seven and eight: dynamic programming and review
Dynamic programming is the topic people fear most, so leave it until your foundations are solid. The core idea is simple: the answer is built from answers to smaller versions of the same problem, and you store those to avoid recomputing. The reliable way in is to write the plain recursion first, confirm it is correct, then add memoisation, then convert to a table if time allows. Jumping straight to a table is where people get stuck, because they try to find the loop order before they understand the recurrence.
def coin_change(coins, amount):
dp = [0] + [float("inf")] * amount
for a in range(1, amount + 1):
for c in coins:
if c <= a:
dp[a] = min(dp[a], dp[a - c] + 1)
return dp[amount] if dp[amount] != float("inf") else -1For each DP problem, force yourself to name two things out loud before writing anything: the state (what does each entry in your table represent?) and the transition (how does one state depend on smaller ones?). For coin change the state is "fewest coins to make amount a" and the transition is "try each coin and take the best of the subproblems." If you cannot articulate those two sentences, you do not yet understand the problem, and no amount of code will fix that.
Spend the final week on review, not new material. Pull every problem from your log that you needed a hint on and redo it from scratch. This is the highest value work in the whole plan, because it converts shaky topics into solid ones right before the interview. Resist the urge to start a brand new topic in the last week. The marginal new problem is worth far less than the firmed-up weak spot.
A worked example: thinking out loud
The interview is a spoken exercise, so here is what a strong first few minutes sounds like on the shipping-capacity problem from the binary-search section, which is the kind of prompt that hides its structure. The task: given package weights that must ship in order, find the smallest daily capacity that clears them all within a fixed number of days.
Candidate: Let me restate it. I have a list of package weights, I must ship them in the given order, and each day I load packages onto a boat without exceeding its capacity. I want the minimum capacity that finishes within, say, five days. For weights one through ten and five days, I think the answer is fifteen. Is that the kind of input?
Interviewer: Yes, that example is right.
Candidate: The brute force is to try every capacity from the largest single weight up to the total, and simulate. Simulation is O(n), and the capacity range is the sum of weights, so that is O(n times sum), which is slow. But notice the structure: if a given capacity finishes in time, every larger capacity also does, and if one is too small, every smaller one is too. Feasibility is monotonic. So I do not search the input, I binary search the answer. The low bound is the heaviest single package, since the boat must at least carry that, and the high bound is the total weight, which always finishes in one day. I binary search that range, and for each candidate I run the O(n) feasibility check. That is O(n log(sum)) time and O(1) extra space. Shall I code the feasibility check first?
Notice what happened. The candidate restated the problem and confirmed an example, named the brute force and its cost, spotted that the real lever is monotonic feasibility rather than a sorted array, justified the bounds of the search, and asked before coding. That sequence is worth rehearsing until it is automatic, because it is the same regardless of the specific problem. Silent coding, even when correct, reads as a weakness, and many loops fail not on the algorithm but on the communication around it.
Why the same problem scores differently
Two candidates can hand in identical correct code and walk away with very different scores, because the rubric weights more than correctness. The same coin-change solution reads as a pass for a new grad and as a thin performance for a staff candidate, and knowing which signal your level is graded on tells you where to spend the practice that this plan frees up.
| Level | What carries the most weight |
|---|---|
| Junior or new grad | Correct solution, clean code, can explain complexity |
| Mid level | Reaches the optimal approach with little hinting, handles edge cases unprompted |
| Senior and above | Communication, tradeoff reasoning, clarifying scope, recovering gracefully when stuck |
At senior level the algorithm is close to table stakes. What distinguishes candidates is how they navigate ambiguity, how they reason about tradeoffs ("a heap gives O(n log k) but a full sort is simpler and fine for this input size"), and how they behave when something goes wrong. A senior who hits a bug, narrows it calmly with a small example, and fixes it often scores higher than a junior who happened to get a clean run, because debugging under pressure is the real job.
The track you are targeting also shifts what shows up. Backend and infrastructure loops lean harder on classic data structures and complexity reasoning, so weight your hours toward graphs, heaps, and binary search. Frontend loops still ask these but often pair them with practical coding, asynchronous reasoning, and the occasional DOM or rendering problem, so a frontend coding interview prep read pays off more there than another graph problem would. Map your practice to the interview questions for your specific role rather than grinding a generic set.
Avoiding the common traps
- Chasing problem count. Fifty problems understood deeply beat three hundred solved once and forgotten.
- Skipping your weak topics because they are uncomfortable. Those are exactly the ones the interviewer will find.
- Memorising solutions instead of patterns. New questions will not match what you memorised, and interviewers can tell when you are reciting.
- Practising only in silence. The interview is a spoken exercise, so rehearse it that way, ideally with a friend or a mock interview tool.
- Optimising too early. Get a correct brute force on the table first, state its complexity, then improve. Many candidates lose the question by chasing the perfect solution and never producing a working one.
- Neglecting edge cases. Empty input, a single element, all-equal elements, and integer overflow in some languages are the cases interviewers reach for. Build the habit of naming them before you finish.
How to practise each problem
The way you practise matters as much as the volume. For each problem, give yourself a fixed time, say thirty minutes, to solve it unaided. If you are stuck after that, read the approach, then close it and implement the solution yourself without copying. The next day, redo any problem you needed help with. This spaced repetition is what moves a pattern from "I have seen it" to "I can produce it cold."
Talk through your approach out loud as if an interviewer were present. State the pattern, the approach, and the complexity before writing code. Treat clarifying questions as part of the work, not a preamble to skip. Asking about input size, value ranges, and whether the input can be empty both prevents bugs and signals the kind of care interviewers want to see.
Frequently asked questions
How many problems do I actually need? Roughly eighty to a hundred well understood problems across the patterns is plenty for most loops. The number matters far less than whether you can recognise the pattern on a fresh problem.
Should I use spaced repetition flashcards for patterns? It helps for recall of the templates (the sliding window shape, the BFS skeleton), but flashcards cannot replace solving. Use them to keep templates warm, not as the main practice.
Is it cheating to look at the solution? No, as long as you then close it and reimplement from scratch, and you log that you needed help so you revisit it. Reading solutions without reimplementing is where the trap lies.
What if I freeze in the real interview? Fall back to the script: restate the problem, give a brute force, then improve. Producing a working brute force and narrating your thinking is far better than a long silence, and it often unsticks you.
Do I need to memorise algorithm proofs? No. You need to be able to justify correctness and complexity in plain language. Formal proofs are rarely asked outside specialised research roles.
Follow the plan, keep the log honest, and review your weak spots at the end. Eight weeks of focused, pattern-led practice will leave most new questions feeling familiar within the first minute, which is the calm you want when it counts.
Sources
- Time complexity of Python built-in types, python.org wiki. The reference for the list, dict, set, and deque operation costs that decide whether your traversal is O(n) or O(n squared).
- Big O notation, Wikipedia. The formal grounding for the time and space complexity you state out loud before coding.
Put the plan to work
Take the patterns into structured practice and real role questions:
- Coding interview patterns, the catalogue of recurring shapes this plan trains you to recognise.
- Blind 75, a compact problem set to work the plan against without drowning in volume.
- Mock interview practice plan, for rehearsing the spoken side of the worked example above.