How to Practise LeetCode Without Memorising Solutions
Learn how to practise LeetCode without memorising solutions using pattern recall, spaced review, fresh variants, and a readiness test for interviews.

If you want to know how to practise LeetCode without memorising, change what each session asks you to retrieve. Do not retrieve finished code. Retrieve the problem shape, the missing insight, the invariant, and the trade-offs that lead to the code.
That shift turns repetition into algorithm pattern recognition. It also prepares you to explain an approach, defend its complexity, and adapt it when an interviewer changes the problem.
Why LeetCode solutions become easy to memorise accidentally#
A solution becomes familiar much faster than the reasoning behind it becomes reusable.
You read a prompt, struggle for a while, and open the editorial. The explanation makes sense. You copy the implementation, run it, and solve the same problem again the next day. This time the code appears quickly.
That feels like progress. It may only be recognition.
The distinction matters:
- Prompt memory tells you that you have seen this problem.
- Code memory gives you familiar lines and variable names.
- Pattern understanding tells you why a certain state, data structure, or traversal fits the constraints.
- Transfer lets you adapt that reasoning when the prompt changes.
Rereading creates false fluency because the solution remains visible while you evaluate your understanding. Immediate repetition has a similar problem. You are often replaying recent syntax rather than deriving the method.
Watch for these warning signs:
- You remember the first line of code before you can state the brute-force approach.
- You know that a problem uses a heap, but cannot explain what the heap represents.
- You can reproduce the original solution but fail when names or examples change.
- You cannot identify the invariant maintained by the loop.
- You state a complexity from memory but cannot derive it.
- A small constraint change leaves you without a starting point.
- You need the original variable names to reconstruct the implementation.
A familiar prompt is not a reliable test. An unfamiliar variant is better because it removes cues from the original answer.
Your goal is not to forget every implementation detail. Some syntax and common templates should become automatic. The problem starts when remembered syntax replaces reasoning.
What you should remember instead of the solution#
Remember a compact chain of decisions that can regenerate the solution.
For each problem, retrieve these elements:
- Problem shape: What is being queried, counted, optimised, or constructed?
- Constraints: What input size, ordering, mutability, and memory limits matter?
- Brute-force baseline: What direct method would work with enough time?
- Bottleneck: Which repeated work makes the baseline too expensive?
- Key invariant: What remains true while the efficient algorithm runs?
- Data structure: What information must you store to maintain that invariant?
- Complexity: How many times does each item enter, leave, or update the state?
- Failure cases: Which boundaries, duplicates, zeros, or empty inputs need attention?
Reduce your notes to those decisions. Do not preserve a polished implementation.
A compact pattern note might look like this:
Trigger: Each output depends on values to its left and right.
Baseline: Recompute both sides for every index.
Reuse: Accumulate one side in the output and carry the other in a scalar.
Invariant: Before processing indexi, the scalar contains the aggregate strictly to its right.
Check: Empty side uses the operation’s identity value.
Cost: Linear time. Constant auxiliary space when the output does not count.
This note forces you to rebuild the loops. A copied function does not.
Useful template knowledge is more general. You should know the shape of binary search, breadth-first search, depth-first search, and a sliding window. You should also know their preconditions.
For example, memorising while left <= right is not enough. You need to know:
- What search interval the pointers describe.
- Whether the interval is closed or half-open.
- Which predicate is monotonic.
- Why discarding one side cannot remove the answer.
- What the loop returns when no exact match exists.
Templates reduce implementation noise. Problem-specific memorisation hides the reason the template applies.
How to practise LeetCode without memorising: use an attempt, review, reconstruct, vary practice loop#
Use the same five-stage loop for each problem: attempt, review, reconstruct, vary, and retrieve later.
Attempt the problem cold#
Start without notes, hints, or old code. Write down:
- The brute-force approach.
- The expected complexity.
- The constraint that makes it inadequate.
- Any pattern candidates.
- The exact point where your reasoning stops.
That final item is important. “Could not solve” tells you little. “Could not find a state that avoids rescanning the left side” identifies a missing idea.
Set a reasonable working window, but do not reduce practice to a timer. Stay long enough to test ideas. Stop when you are cycling through the same thoughts without producing new information.
Review only the missing idea#
Consult the smallest useful amount of explanation.
If you missed the pattern, read until you can name it. If you found the algorithm but mishandled a boundary, inspect that boundary. If you cannot justify the complexity, review the operation count rather than the entire implementation.
Then close the solution.
Do not copy code into your editor. Copying changes the task from problem solving to transcription.
Reconstruct from the invariant#
Before writing syntax, state the invariant in one or two sentences. Derive the control flow from it.
For a window problem, that might be:
The current window is the longest valid window ending at
right. Moveleftonly while the validity condition is broken.
Now write pseudocode. Then implement it.
If you get stuck on syntax, look up syntax rather than the original answer. The purpose is to preserve the reasoning step while removing irrelevant friction.
Vary an important condition#
Change something that affects the algorithm:
- Make the input mutable.
- Require indices instead of a value.
- Add duplicates or zeros.
- Ask for all valid outputs instead of one.
- Replace an offline input with a stream.
- Tighten the memory limit.
- Request repeated queries.
- Change the tie-breaking rule.
Do not settle for renaming variables. Coding problem variation practice works when the variation threatens an assumption in the original method.
Retrieve the idea later#
Schedule later attempts with increasing gaps. On each attempt, begin from a blank file and a short prompt.
This is spaced repetition for coding, but the repeated unit is not the source code. It is the reasoning chain.
A useful retrieval sequence is:
- Explain the approach without code.
- State the invariant and complexity.
- Write pseudocode.
- Implement from the pseudocode.
- Solve a related problem with fewer surface cues.
That is LeetCode active recall. It tests whether you can produce the method rather than recognise it.
Worked example: derive Product of Array Except Self#
LeetCode 238, Product of Array Except Self is best learned by deriving reusable partial products, not by memorising two loops.
For each index i, you need the product of every element except nums[i].
Start with the quadratic baseline#
The direct method fixes an index and scans the entire array, skipping that index.
For an array of length n, you perform roughly n work for each of n outputs. The time complexity is O(n²).
Division appears to offer a shortcut:
- Compute the product of the whole array.
- Divide it by
nums[i]for each output.
That is not a general solution. A zero makes division invalid. Multiple zeros require additional branching. The problem also asks you to avoid division.
The exclusion requirement suggests a better decomposition.
For index i, every included value lies in one of two regions:
- Strictly left of
i. - Strictly right of
i.
Therefore:
answer[i] = product(left of i) * product(right of i)You can compute all left products in one pass and all right products in another.
Derive the two passes#
During the left-to-right pass, store the product strictly before each index in the output array.
During the right-to-left pass, maintain a running suffix product. Multiply it into the existing left product.
def product_except_self(nums):
answer = [1] * len(nums)
prefix = 1
for i in range(len(nums)):
answer[i] = prefix
prefix *= nums[i]
suffix = 1
for i in range(len(nums) - 1, -1, -1):
answer[i] *= suffix
suffix *= nums[i]
return answerThe value 1 is the multiplicative identity. It represents the product of an empty side at either end of the array.
Trace a concrete input#
Use nums = [1, 2, 3, 4].
The output starts as:
[1, 1, 1, 1]After the prefix pass, each position contains the product strictly to its left:
[1, 1, 2, 6]The states mean:
- Index
0: nothing lies to the left, so store1. - Index
1: the left product is1. - Index
2: the left product is1 * 2 = 2. - Index
3: the left product is1 * 2 * 3 = 6.
Now move from right to left.
Start with suffix = 1:
- At index
3, multiply by1. Output remains[1, 1, 2, 6]. Updatesuffixto4. - At index
2, multiply by4. Output becomes[1, 1, 8, 6]. Updatesuffixto12. - At index
1, multiply by12. Output becomes[1, 12, 8, 6]. Updatesuffixto24. - At index
0, multiply by24. Output becomes[24, 12, 8, 6].
The invariant during the reverse pass is precise:
Before processing index
i,suffixequals the product of all elements strictly to the right ofi.
Each element participates in a constant amount of work, so the time complexity is O(n). The output array uses O(n) storage. The algorithm uses O(1) auxiliary space when the required output storage is excluded from the space calculation.
Turn one solved problem into a variation ladder#
A variation ladder tests one assumption at a time until the original code no longer transfers directly.
For Product of Array Except Self, start with verbal changes:
-
Allow one or more zeros.
Does the prefix-and-suffix method still work? It does. Which alternative fails? The division method. -
Allow negative values.
Does the invariant change? No. You should still check whether you made an unstated assumption about positive products. -
Require results for selected indices only.
Would constructing the complete output still make sense? The answer depends on the number of queries and whether the input changes. -
Support repeated point updates.
A static prefix pass becomes stale after an update. You now need to reason about dynamic range products, zero counts, and an appropriate query structure. -
Restrict numeric width.
Intermediate products may overflow in fixed-width languages. You must clarify whether the task expects modular arithmetic, arbitrary-precision integers, or guaranteed safe inputs. -
Change the aggregation operation.
Ask whether the same two-pass structure works for sums, minimums, maximums, or another associative operation. Check whether an identity value exists and whether combining partial states is sufficient.
Next, turn one variation into code. Do not merely answer it verbally. Implementation exposes assumptions that an explanation can hide.
Then move to an unseen prompt that uses related left/right accumulation or precomputed range information. The prefix sum pattern hub is a useful next stop even though products and sums are different operations. Focus on the shared idea: preserve reusable information about a prefix instead of rescanning it.
You can also browse the broader LeetCode pattern reference or search the problem reference for practice. Choose problems by structural similarity, not by title similarity.
Test whether you understand a problem without rerunning it#
You understand a solution when you can reproduce and challenge its reasoning without executing the code.
Use four tests.
Explain the approach aloud#
Give yourself the same explanation burden you would face in an interview:
- State the direct approach.
- Identify its bottleneck.
- Introduce the improved state or data structure.
- Define the invariant.
- Explain why each update preserves it.
- Derive time and space complexity.
Avoid saying, “This is just a standard pattern.” Name the pattern, then explain why it applies here.
Trace an edge case#
Choose an input that threatens the algorithm’s assumptions:
- Empty input, if allowed.
- One element.
- Duplicate values.
- All equal values.
- A zero near each boundary.
- A strictly increasing or decreasing sequence.
Predict every state transition before running anything. If your prediction and implementation disagree, investigate the invariant first.
Write pseudocode before syntax#
Pseudocode reveals whether you know the algorithm independently of a language.
For every data structure, ask:
- What does it store?
- Why do I need it?
- When does it change?
- Can it contain stale information?
- What operation determines the complexity?
If you cannot justify a stack, map, queue, heap, or auxiliary array, it may have entered the solution through memory rather than necessity.
Compare against brute force#
State when the simple approach would be acceptable.
A quadratic method may be reasonable for small inputs, a one-off script, or a first correctness pass. The optimised method earns its complexity only when constraints require it.
This comparison keeps your complexity analysis grounded. It also gives you a recovery path if the optimised idea does not arrive immediately.
Build review notes that do not become an answer bank#
Good review notes hide enough information to force retrieval.
Store:
- The pattern trigger.
- The brute-force bottleneck.
- The key invariant.
- The mistake you made.
- Important edge cases.
- Time and space complexity.
- One variation that would break or alter the approach.
Do not store the complete implementation. If you need a syntax reminder, keep it in a separate language reference rather than attaching it to the problem.
Use prompts instead of explanations:
- “What repeated work can be reused?”
- “What is true before each iteration?”
- “Why can the left pointer move only forward?”
- “What does the stack contain?”
- “Which constraint rules out the direct approach?”
- “How does the method behave with duplicates?”
- “What changes if updates arrive between queries?”
Tag mistakes by cause:
- Missed constraint: You ignored an input bound or mutability requirement.
- Wrong pattern: You matched surface wording rather than structure.
- Broken invariant: Your state did not represent what you claimed.
- Boundary handling: You mishandled an empty range, endpoint, or loop condition.
- Implementation detail: The reasoning was sound, but indexing or syntax failed.
- Complexity error: You overlooked repeated work or hidden storage.
These tags improve your LeetCode study method because they tell you what to practise next. Repeated boundary errors call for dry runs. Repeated pattern errors call for classification and variation work. Repeated syntax errors call for small implementation drills, not more editorials.
Know when a problem is genuinely learned#
Treat transfer to an unfamiliar variant as the main test of whether a problem is learned.
You should be able to:
- Identify the problem shape without relying on its title.
- Produce a brute-force baseline.
- Explain the bottleneck.
- State and defend the invariant.
- Dry-run an edge case correctly.
- Derive the complexity.
- Write code without consulting the prior answer.
- Adapt the method when one important assumption changes.
A correct rerun of the original prompt is supporting evidence, not the final standard. The original prompt contains too many memory cues.
For coding interview readiness, add an explanation test. Talk while you derive the solution. State uncertainty when you have it. If your first idea fails, explain why and revise it. That is closer to the actual task than silently reproducing polished code.
After a practice attempt, Stealth Interview can read the problem from a screenshot and return a solution with a step-by-step explanation and time and space complexity. Use that output for comparison after you have committed to your own reasoning. Check where the approaches differ, close the reference, and reconstruct the method yourself.
Retrieval remains the core exercise. A reference can expose a missing idea. Only a fresh explanation, dry run, implementation, and variation can show that the idea is now yours to use.
Frequently asked questions
- How do you practise LeetCode without memorising solutions?
- Use a five-stage loop: attempt the problem cold, review only the missing idea, reconstruct from the invariant, vary an important condition, and retrieve the reasoning later. Start each later attempt from a blank file rather than replaying prior code.
- What should you remember instead of LeetCode code?
- Remember the problem shape, constraints, brute-force baseline, bottleneck, invariant, required data structure, complexity, and failure cases. This decision chain should let you regenerate the implementation.
- How can you tell whether you understand a LeetCode solution?
- Explain the approach and invariant, trace an edge case, derive the complexity, write pseudocode before syntax, and compare the method with brute force. The main test is whether you can adapt the reasoning to an unfamiliar variant.
- Should LeetCode review notes include complete solutions?
- No. Store prompts, pattern triggers, bottlenecks, invariants, mistakes, edge cases, complexity, and a variation that changes the approach, but keep complete implementations out of the notes.
- How should you use a LeetCode editorial without copying it?
- Read only enough to identify the missing pattern, boundary detail, or complexity argument, then close the solution. State the invariant, write pseudocode, and implement from your own reconstruction.
Keep reading

LeetCode 75: A Pattern-Based Study Plan That Works
Turn LeetCode 75 into a pattern-based curriculum using grouped practice, delayed re-solves, mixed review, and clear reasoning.

Grokking the Coding Interview: A Practical Study Guide
Use Grokking the Coding Interview as a pattern-first framework built on cold attempts, retrieval, variations, and mixed practice.

Coding Interview Questions With Worked Answers and Code
A pattern-based question bank with worked solutions, complexity analysis, testing guidance, and a framework for reasoning aloud.