How to LeetCode: What I Learned from a Year of LeetCoding Challenge Problems

image

(My submission graph since the start of the LeetCoding challenge on April 1, 2020. Although I submitted a challenge solution every day, there are a few blanks because of the PT vs. UTC issue).

The 30-day LeetCoding challenge

We're approaching one year of daily challenges, which started (no joke) on April 1, 2020 as the 30-Day LeetCoding Challenge. Like many of you, I've been using the challenges to get better at coding problems. To commemorate the anniversary, here are my thoughts on the best way to study LeetCode problems.

The daily challenge problem

It's convenient to get up every day and open a problem that’s chosen for you, knowing that fellow LeetCoders are also working on it. You know there will be activity in the discussions, and Dmitry Babichev will be hard at work on a daily editorial. So the daily challenge is a way to motivate yourself and ensure that you solve at least one problem per day.

I look at the daily challenge as a one-problem unrated contest. Like a contest problem, it appears at a specified time, enforces a time limit, and covers an arbitrary topic that you don't know in advance. Having a fixed start time gets you in the habit of checking LeetCode every day and working on a problem. The time limit encourages you to submit a solution before the end of the day rather than putting it off for later. And the arbitrary topic is a better simulation of an interview than solving problems by tag, since you must come up with the approach yourself without a hint about which algorithm to use. It also exposes you to problems in different areas, not just your favorite ones.

But just as participating in the weekly contest (and doing nothing else) wouldn't be the most efficient way to get better at LeetCode, doing the daily challenge problem isn't a complete study strategy on its own. For that, I recommend spaced repetition using model solutions.

Spaced repetition

Spaced repetition is a well-studied technique that works for learning anything, including coding problems. Here's the idea: Although you naturally forget information over time, you can disrupt the forgetting process by studying information on a schedule, with the gap between review sessions increasing over time as you improve.

The key decision you have to make when using spaced repetition is when to schedule the repetitions. For example, consider LeetCode 987: Vertical Order Traversal of a Binary Tree, a LC Hard problem that appeared as the daily challenge on Friday, January 29, 2021. If you're a diligent LeetCoding Challenger, that problem might have looked familiar in January, because it was a repeat: It was also the daily challenge problem on Friday, August 7, 2020. When I solved it in January, it took me about an hour — not terrible for a Hard problem, but not good enough for an interview. So if my goal was to learn this problem thorougly, 25 weeks between repetitions would be too long.

There's plenty of spaced repetition software that will calculate repetition intervals for you. But for LeetCode practice, I'm using a simpler approach: For problems I want to practice seriously, I use a spreadsheet to record the problem URL, a timestamp each time I practice it, and how much time elapsed before I got a correct solution. Based on how each practice session goes, I manually enter a number (in days) indicating when I think I should practice it next. The spreadsheet calculates how long it has been since the most recent solution, and highlights problems that are due for practice.

image
(A section of my spreadsheet. Yes, it can take a few tries to learn these problems completely).

When I'm first learning a problem, I set the repetition interval to one day, and just focus on that problem. Once I can solve it reasonably well two days in a row, I leave it for a few days and work on other problems. If I can still solve it after a few days, I try waiting a week, a couple of weeks, a month, and so on. At some point, there are diminishing returns to returning to the same problem, and I find it best to remove it from my practice list, even if I don't know it perfectly. My goal is to learn a problem well enough that I can reconstruct the solution in a reasonable amount of time. At that point, my efforts are better spent elsewhere. How much time is reasonable depends on the problem difficulty, keeping in mind that an interview lasts about an hour and may contain multiple problems.

Model solutions

Traditional spaced repetition uses flashcards (paper or electronic) that have a prompt on one side and an answer on the other. For LeetCode, we're not trying to memorize lines of code. But it's still important to have a specific goal for what it means to learn a problem, besides just getting an accepted submission. I use the concept of a model solution.

For each LeetCode problem that I'm practicing with spaced repetition, my model solution is the best solution I know of for that problem, assuming the goal is to write the solution on a whiteboard and explain it verbally to an interviewer. With that in mind, here are some characteristics of a useful model solution:

  • It's correct, and reasonably efficient in time and space complexity. It doesn't have to be optimally efficient, since the most efficient solution may be overly complicated for an interview. For example, the free official solution for 673: Number of Longest Increasing Subsequence offers two approaches: A Dynamic Programming solution that runs in O(n^2) time, and a segment tree solution that runs in O(n log n) time. If those two solutions were the only ones I knew about, I would go with the relative simplicity of the DP solution over the more efficient solution.

  • Shorter is better if it doesn't hurt readability. I don't use one-line solutions for model solutions unless the problem is very easy.

  • For problems that come in series, it's best to learn a solution that applies to all the problems in the series, if that solution doesn't conflict with the other rules. As an example of how to do that, see this excellent article about the Best Time to Buy and Sell Stock problems.

How do you find a good model solution? One of LeetCode's strengths compared to its online and offline competitors is community involvement. Even for problems with locked premium solutions, there are always plenty of ideas in the discussions. And as you practice a problem repeatedly, you can adjust your model solution based on your experience with it. If you have trouble remembering or explaining parts of it, see if you can simplify the solution.

The LeetCode online judge only cares about the code, but a model solution is more than just code. To understand and remember a solution, explain it to an interviewer, and apply it to similar problems, your model solution needs a few parts. I use these sections when writing a model solution:

  • Restatement of the problem description. Sometimes the official description is unclear or misleading.

  • Explanation of any aspects of the problem statement that aren't spelled out directly in the official description, but are useful for solving the problem.

  • The solution idea: A brief statement of how the solution works, including which algorithms or data structures to use, but not including every detail.

  • A full description of the solution, explaining each step thoroughly.

  • Definitions for the main variables used in the solution.

  • Pseudocode solution: a language-agnostic solution, covering all the logic but avoiding language-specific syntax.

  • The solution in a real programming language, runnable in LeetCode.

If you want to see examples of this model solution structure, I have published a few online.

There are a few reasons to spend the time to write out a solution like this. First, describing a solution comprehensively makes you aware of any parts you don't fully understand. Second, it's a way to polish the solution before you learn it. Finally, the full solution, not just the code, is what you want to learn, and the written solution gives you an artifact to study.

How long to work on a problem

I said the daily challenge problem was like a one-question unrated contest. Using that analogy, the contest time limit is 24 hours: A problem appears every day at midnight Pacific Time, and you have to submit it before midnight the next day to get credit for that day's Challenge.

But although you could spend 24 hours on each problem, I wouldn't recommend it. Besides sleep deprivation and neglecting other parts of your life, 24 hours is just too much time to spend on any LeetCode problem, even spread out over several days. In fact, I would recommend spending much less: just one hour per problem, maybe two hours if you're still making progress after an hour, or if you have several hours per day available to practice LeetCode. Once you hit this time limit, start looking for hints in the problem or the discussion boards.

Why stop after an hour or two? Isn't it good experience to struggle with a coding problem? That may be true for some types of problems, but it isn't true for these problems. LeetCode specializes in problems that an interviewer will ask you to solve as some fraction of an hour-long interview. If you can't solve them in an hour or so, you're probably missing some key insight or technique.

When I check the discussion board after submitting a daily challenge problem, the solutions I find are usually more efficient and concise than my solution. The exception is when I'm able to adapt one of the model solutions that I previously learned. And that's the point of limiting the time you spend on each problem. When you get stuck on a problem, you could spend an extra hour or more finishing a less than optimal solution. Or you could instead crowdsource the best solution that LeetCoders have come up with for the problem, and spend the extra time studying and learning it. Then when you get a similar problem in practice or in an interview, you'll have an excellent solution to draw from.

Comments (4)