To practice LeetCode effectively, you have to choose the correct problem difficulty. If you start with Hard problems when you’re first learning a topic, you won’t learn much because you won’t be able to make much progress on the solution, so you won’t get a chance to practice the topic. At the other extreme, if you practice Easy problems when you already know a topic well, you won’t learn much because you won’t challenge yourself.
But problem difficulty is not the only way to adjust the difficulty of your practice. You can also use other aspects of your practice routine to find the optimal difficulty level.
To learn more about the ideas in this tip, see Desirable Difficulties: When Harder is Better for Learning by Scott Young.
Desirable difficulty is the idea that making learning harder can make your study process more effective. For example, switching from Easy to Medium problems for a topic that you have some experience with will make it more difficult to solve each problem. But the additional challenge provided by Medium problems will push you to learn more about each topic than you would by solving Easy problems.
Once you select a problem with the correct difficulty level, you can further find-tune the level of practice difficulty using these techniques:
If you practice a problem until you solve it, then delete your code and immediately write it again from scratch, it will be easier to solve the second time. Since your first attempt was just a few minutes earlier, you’ll easily recall some details. If instead you wait a week to make your second attempt, you’ll find it more difficult to come up with the solution. But in the long run, you’ll make more progress if you space out your practice. This is the idea behind spaced repetition.
If you practice a sequence of binary search problems at a similar difficulty level, the problems will get easier over time. Each problem will give you more experience with binary search, and you’ll be able to concentrate on the solution patterns required for binary search, with no distractions from other problem types. If instead you alternate between binary search problems and binary search tree problems, it will make your practice more difficult. When you solve a binary search tree problem, you’ll displace a bit of what you remember from binary search, and vice versa. It will take longer to become proficient at both topics than it would if you learned them sequentially. But in the long run, interleaving topics will help you gain a stronger mastery of each topic.
Reading someone else’s solution is much less effective for learning a topic than solving a problem on your own, even if you are reproducing a model solution. The process of implementing a solution gives you experience that you can’t get just by studying code. This is known as the testing effect.
Although research supports the effectiveness of spacing, variability, and testing, there isn’t a consensus about why these techniques work. One theory is that the brain maintains a retrieval strength for each memory. You can recall memories (like how to implement binary search) more easily when the connection to that memory is stronger. The brain strengthens the connection when it detects that a stronger connection is needed. If you retrieve a memory easily because you used it recently to solve a problem (low spacing), because you have been practicing similar problems (low variability), or because you are just reading the solution (no testing), your brain concludes that the connection strength is fine. On the other hand, if you have to struggle to recall a key concept but you eventually retrieve it, that’s a signal that the concept is important, so the brain strengthens the connection.
There’s another theory explaining why variability is useful for learning. When you interleave similar problems, your brain has to distinguish between them. For example, consider binary search problems and binary search tree problems. In both cases, you need to find a target element by moving left or right in an ordered data structure, eliminating half the search space on each iteration. But for binary search, you’re using an array, so you can jump to any location in constant time, while for the BST, you are traversing tree nodes one at a time as you search for the target node. Solving a binary search tree problem after having recent experience with binary search forces you to think more carefully about which techniques to apply at each point in the algorithm, and which algorithm is most useful for the problem.
This year, I’m publishing a series of tips for effective LeetCode practice. To read the tips in order, start with A Project for 2023.