Red-Green-Code

Deliberate practice techniques for software developers

  • Home
  • About
  • Contact
  • Project 462
  • CP FAQ
  • Newsletter

LeetCode Tip 40: Learn Dynamic Programming

By Duncan Smith Leave a Comment Oct 11 0

LeetCode 2023

In the context of algorithms, dynamic programming is a technique for solving a certain type of problem by breaking it into subproblems, solving those subproblems, and using the results to find the solution to the original problem.

The purpose of this tip isn’t to explain how dynamic programming works. There are already more than enough free online resources for that purpose. Here are a few:

  • A short TopCoder tutorial: Dynamic Programming: From Novice to Advanced
  • Another short tutorial on Brilliant: Dynamic Programming
  • If you really like math: Dynamic Programming, Chapter 11 from the textbook Applied Mathematical Programming by Bradley, Hax, and Magnanti.
  • If you prefer video tutorials: Dynamic Programming by Tushar Roy (Coding Made Simple) on YouTube.
  • If you already know the basics and just want sample code: Dynamic Programming Patterns by Atalyk Akash on LeetCode.

The reason I’m bringing up dynamic programming is because, more than any other technique, it illustrates the difference between learning a solution and learning a process to find the solution.

The previous tip has an example of a DP solution pattern that applies to a specific problem type, but isn’t applicable to every DP problem. Although learning specific patterns is a good way to solve certain types of problems quickly, it’s not a viable approach for learning every LeetCode problem type. Instead, we want a process for finding a solution to any DP problem, even when one of our ready-made patterns doesn’t apply.

The rest of this tip assumes that you have some familiarity with Dynamic Programming.

Check if the technique is applicable

One sign you can use DP for a problem is that you can write a function solve(n) and inside that function you can use the result of solve(n-x) for some x. But rather than recalculating solve(n-x) as you would with a purely recursive solution, you store the result and retrieve it when you need it to evaluate solve(n). More formally, we can say that a DP problem has overlapping subproblems and optimal substructure.

Each LeetCode problem type has telltale signs showing which solution method applies. If you learn these signs for each problem type, you’ll have a better chance of knowing which approach to use.

Learn the basic patterns

To calculate solve(n), you could call solve(n-x). Then solve(n-x) could check if the result has already been calculated, and either retrieve it or calculate and store it. Alternatively, you could write a loop that iterates from 0 to n and each iteration could either use a result that has already been calculated or calculate and store the result. For DP, these two approaches are called top-down (using recursion) or bottom-up (using iteration).

Learning a basic algorithmic pattern differs from learning a specific solution, like the DP solution to the House Robber problem. Top-down and bottom-up are basic patterns, meaning every DP solution uses one of them. Similarly, other LeetCode problem types have their own patterns that define the type. For example, every binary search solution has the same fundamental structure, even if the details are different.

Apply a framework

Once you select a pattern, fill in the details using a framework. A framework helps you identify key information in the problem as you’re implementing your solution. For DP, the key information is state, which you can think of as the parameters to your solve function. For solve(n), the state is described by one variable, n. More complicated DP problems require more variables to describe the state.

Each problem type framework has details to look for. For DP, we have state variables. For binary search, we have the the range to search and how to find the midpoint. For graph problems, we have to figure out how to represent the problem data as edges and nodes.

Practice the framework using specific problems

Now we’re back in model problem and model solution territory. To learn a framework, you need to solve problems. But when you approach problems from the more general perspective of a framework rather than a specific solution example, it’s easier to apply what you know to new problems. Dynamic programming is a good place to apply this approach because it’s a flexible technique rather than a specific algorithm. It’s also easy to find tutorials that walk through the process of analyzing DP problems. And when it’s time to practice, LeetCode has plenty of DP problems. Then, once you’re comfortable with dynamic programming, you can apply the pattern/framework approach to other LeetCode problem types.

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.

LeetCode Tip 39: Learn the Process of Finding the Solution

By Duncan Smith Leave a Comment Oct 4 0

LeetCode 2023

Learning a model solution gives you a tool to solve problems of a particular type. If you write and study a model solution for binary search, you’ll be able to solve straightforward binary search problems. Doing the same thing with other common solution types will make you better than average at coding interview problems.

But not every problem matches a standard solution approach. Hard problems require non-trivial insights, and even Medium problems often need customized solutions that build on top of a standard approach. So, while knowing model solutions will give you a head start, it won’t be enough to tackle every problem confidently. After mastering the model solution for a problem type, the next step is to learn how someone discovered that solution.

Dynamic Programming (DP) provides a good example of the difference between knowing how to solve a problem and knowing how to discover the solution. Consider the LeetCode DP problem House Robber. The scenario: A robber is planning to rob a row of houses and they know how much money is in each house. If they’re not allowed to rob two adjacent houses, what is the maximum amount of money they can steal?

This problem has a short solution, though it’s not simple to come up with. The idea is to track two values, dp1 and dp2, which both start at 0. We move through the houses from left to right. dp2 always stores the current maximum amount, the best answer we have found so far. dp1 stores the previous value of dp2 — i.e., the second best answer. At each house, we add that house’s amount to the second best answer, dp1 and see if it beats the best answer, dp2. If so, it becomes the new dp2. If not, we stay with our current dp2. Since we can’t use adjacent houses, we’re not allowed to add the current house’s value to dp2 to get an even larger amount.

To implement this algorithm, we loop through the house list and execute a modified swap operation at each house. At the end, we return the best result, dp2.

for each house in houseList
    temp = dp1
    dp1 = dp2
    dp2 = max(dp2, temp + house)

return dp2

House Robber is worth studying as a model solution because it shows how we can find an optimal solution for an entire row of houses while only considering three values: the current house amount and two sums. We don’t need to remember any previous values, so we only need O(1) space. And since we only process the list once, we only need O(n) time. Quite an efficient algorithm.

This problem is a good example of a kind of Dynamic Programming, and you can apply the solution to other problems. But although the algorithm is simple, it may not be clear how we would invent it from scratch. That’s why we want to consider how to analyze a DP problem, not just how to solve it. We’ll look at that process in the next tip.

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.

LeetCode Tip 38: Write a Code Notebook

By Duncan Smith Leave a Comment Sep 27 0

LeetCode 2023

As we saw in Tip 31: Learn Problems and Sub-Problems, you sometimes need to learn a skill that’s smaller than a full LeetCode problem. Tip 31 explains how you can use the standard LeetCode interface to learn concepts at the sub-problem level. But there’s another way to accomplish that goal by using a REPL.

The read–eval–print loop (REPL), a tool from the early days of Lisp programming, lets you execute pieces of a program interactively. Rather than writing an entire program, compiling it, running it, and evaluating the result, you can execute a single line or block of code and see what it does immediately. This speeds up the process of learning language syntax, library functions, and the code blocks that fit together to make an algorithm implementation.

For LeetCode study, the REPL I recommend is the Jupyter Notebook. These notebooks let you write and run blocks of code surrounded by Markdown text, images, equations, and everything else you need to describe a model solution. Visual Studio Code has a convenient Jupyter extension that I use it to write my model solutions and practice journal.

A standard problem editorial, like the ones published on LeetCode, focuses on correct solutions for a particular problem. An editorial may show several ways to solve a problem, maybe with different time and space complexities. But the goal of an editorial is to describe a complete solution, like one that you could submit to LeetCode. Notebooks are a good way to document complete solutions. But they are also good for more exploratory learning.

Using a Jupyter Notebook, you can break a solution into parts and experiment with each one. In addition to “correct” code from the solution, you can show why other variations don’t work. Even runtime errors are shown in the notebook, so you can see what happens when you do something the compiler doesn’t like.

Code comments and naming conventions can help explain what is going on in a model solution. Notebooks take that a step further by letting you embed runnable blocks of code that show exactly what is happening in each part of the solution. And they let you experiment with the code in real time to help you understand exactly how it works.

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.

LeetCode Tip 37: Understand Your Test Cases

By Duncan Smith Leave a Comment Sep 20 0

LeetCode 2023

You can’t debug a LeetCode solution without studying test cases, the textual input that every LeetCode program is required to process. The LeetCode platform verifies that every test case, whether an official test case or one you invent, meets the formatting and range specifications from the problem statement. The platform also gives you the correct output for every input. This platform behavior ensures that LeetCode test cases are reliable. Both the input and output are guaranteed to be correct.

But these platform guarantees don’t mean you can just ignore the test cases. Although you can rely on the correctness of the test cases and test results, understanding a model solution requires understanding the inputs and outputs, and a good model solution should explain the algorithm using specific test case examples.

The simplest way to use test cases is just to take what LeetCode gives you. First, make sure your program passes the sample cases. Then submit it, review the official test case results, and fix any bugs. This approach gets the job done, but it means you’re relying on someone else to design good test data, and only reviewing that data if it exposes bugs in your implementation.

To get more involved in test case production, use the random data approach: Write a short program that generates random numbers, letters, tree nodes, or whatever input the program requires. Paste that data into the LeetCode interface, run your program, and see what happens. This approach encourages you to think about the input. The amount of thinking required depends on the input criteria. For many LeetCode problems, random numbers in the appropriate range, or random lowercase letters from a to z, are good enough. Some problems require a more sophisticated test case generator. For example, if you have to generate a binary search tree, your generator has to understand BST node ordering rules. But spending time on your input generator is time well spent. To generate input, you have to understand the input requirements, which gives you a head start on your design.

Although a random test case generator lets you verify your program with as much input data as you want, you may not have enough time to generate all important inputs. If a program takes integers from $0$ to $2^{31}-1$ as input, you would have to generate a lot of data to have even a 10% chance of producing and testing both $0$ and $2^{31}-1$. (How long that would take is left as an exercise for the reader). So when you generate random data, it’s best to combine it with some hand-crafted test cases to make sure you cover all the input that might cause trouble for your program, like the extreme values in the input range.

Algorithms often perform differently on different inputs, which is why computer scientists study the best-case, worst-case, and average time complexity of algorithms. For example, a naïve QuickSort implementation can take $O(n^2)$ time to sort input that is already sorted! This is another reason to understand your input data. If you generate random data to test your QuickSort implementation, it’s unlikely that you’ll get sorted data. So you have to think about what data will best exercise your algorithm.

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.

LeetCode Tip 36: Debugging Advice

By Duncan Smith Leave a Comment Sep 13 0

LeetCode 2023

LeetCode debugging is different from real-world debugging. But as in the real-world, you sometimes have to debug your LeetCode solutions. So it’s worth learning the techniques that work best for LeetCode-style programming.

According to the model solution approach, the goal of LeetCode practice is not just to get your submission accepted, but to learn the right way to solve problems. So the most basic form of debugging is verifying that your solution follows the design that you planned. Even when you are solving a problem for the first time, make a plan before starting implementation. The first step in debugging is comparing your code to that plan. If you’re repeating a problem, then your goal is to implement a particular model solution. Make sure your code matches what you remember about that solution.

Even if you have a model solution implementation that you could compare with your solution, it may still be worth spending time on debugging. One goal of spaced repetition is to figure out which parts of a solution are difficult, so you can focus your efforts on them. As you debug your solution, you’ll naturally find those parts. This may prompt you to re-write them so that they’re easier to understand. Or they may already be well-written, but through the debugging process, you understand and remember them better.

LeetCode problems are well-defined puzzles that are written to be solved using standard algorithms. In theory, you could even prove that your solution is correct using the tools of formal program verification. But even if you don’t go down that rabbit hole, you can check loop invariants to verify that variables have expected values at certain points. If an invariant is violated, that narrows down where in the code you need to look to find your bug.

If you can’t find any bugs but a test case is still failing, it’s not against the rules to use that test case to figure out what’s wrong with your solution. But you don’t have to use the exact test data that LeetCode gives you. LeetCode offers a feature that lets you copy a failed test case into your local test list. Then you can run it as many times as you need to without cluttering your history with failed runs. You can also modify the test case once it’s in your local list. To make it easier to track down bugs, you can make the test case smaller. Maybe your solution only has trouble with part of the test case. Try removing some data to see if you still get a failure. This approach relates to Stack Overflow’s Minimal, Reproducible Example advice. The less data you need to work with, the easier it will be to find the bug.

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.

LeetCode Tip 35: Debug Prudently

By Duncan Smith Leave a Comment Sep 6 0

LeetCode 2023

When you write a LeetCode solution, it may have bugs. This is one way in which LeetCode programming and real-world programming are similar. But debugging your LeetCode solution doesn’t always work the same way as debugging your real-world code.

In the real world, when someone gives you a task, you’re the one person responsible for completing it. If you get stuck, you can ask your teammates or your AI buddy for help. And other developers may need to review your code before it is accepted into the product. But you are ultimately responsible for it. So when your code has a bug, it’s up to you to diagnose and fix it. It’s also up to you to decide when your code is “done” and ready to submit for review. And you are the one person writing that code for your particular product.

On LeetCode, thousands of people solve each problem, tens of people publish their solution, and one person writes an official editorial. So when you’re debugging your code, you always have the option of checking other solutions to see if you’re on the right track. And unlike real-world advice, which may not apply directly to the problem you’re trying to solve, these other solutions will be highly relevant, since they apply directly to the problem you’re working on.

This means it’s important to ask yourself at each point in the debugging process whether debugging is the best thing you could be doing with your time. If your solution is mostly correct but has a few bugs, it may be worth spending some time debugging, so you have a solution that you wrote yourself from beginning to end. But if your research tells you that your solution is unlikely to work, it’s probably better to cut your losses rather than trying to debug it into submission. Accept that you went off in the wrong direction, and learn a better way to solve the problem.

Another way that LeetCode programming differs from real-world programming is the testing process. In the real world, developers may write unit tests. But these tests, as they are written by the same person who is writing the real-world code, are often imperfect. They may not cover every important use case, or they may succeed when they should fail or fail when they should succeed.

On LeetCode, you can assume that the tests are reliable. LeetCode problems are easier to test than real-world software, since they are well-defined and constrained problems. Test cases are written and reviewed by experts, then reviewed again by LeetCoders as they try out the problems and propose new test data. So unless the problem you’re solving is very new, running your solution against the official tests cases will subject it to a rigorous test process. If your solution passes, you can conclude that it does a good job of solving the problem as specified.

But there are a few things to consider as you use official LeetCode test cases to evaluate your solution. First, unlike with Test-Driven Development (TDD) in the real world, it’s not a good idea to use LeetCode tests to drive the development of your solution. According to the TDD approach, you would run the official tests before you write any code, then write code to make the tests pass. But this isn’t what LeetCode tests are designed for. They are written to test a complete solution, not a single function. Furthermore, if you’re just trying to get tests to pass, you may end up with a solution that works for the small early tests but is not efficient enough to process larger tests fast enough.

So rather than letting tests drive your development, consider from the beginning what data you will need to handle. The “Constraints” section of the problem description will give you this information. This is also how an algorithmic coding interview works. Your interviewer will want you to provide a conceptual design for your solution. You won’t have time to build it iteratively, guided by test cases. (If you get a real-world coding problem in your interview, this advice may not apply. In that case, ask whether you should write unit tests first or at all).

Even if you complete your solution before running it against any test cases, you may still encounter a debugging snag. LeetCode will helpfully tell you that your solution failed on test 31 out of 100, which means it passed the first 30 test cases. It will even give you the test data and expected output, and let you copy the test case to your local tests. That lets you modify your solution and run it as many times as you want until it passes test #31. Once your fix is done, you can submit your solution again. Maybe this time it will fail on test #73. Repeat the process and submit again. Sometimes you’ll have to do this a few times as you discover more bugs in your solution. But make sure you aren’t just adjusting your implementation specifically for these failed tests. Ideally, you want an elegant solution that solves the general problem and avoids special cases. LeetCode test cases are designed to check for particular classes of bugs in your solution. But you want your solution to work on any valid test data because your algorithm is correct, not because you designed it to work on particular test data. So if you get more than one or two failed tests cases, consider going back to your design to see if it needs an update.

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.

LeetCode Tip 34: Write Your Own Textbook

By Duncan Smith Leave a Comment Aug 30 0

LeetCode 2023

The study process that I describe in these tips is based on model problems and model solutions. To write a good model solution, you can learn a process for writing it and the format that makes it most useful. The goal of LeetCode practice is to learn all the common problem types. Model solutions best support this goal when you write them in such a way that each concept is memorable. There are various techniques to make it easier to remember the concepts.

The last topic we need to consider for model solutions is the target audience. When an author writes an official LeetCode editorial or an unofficial solution in the discussion forums, they write it for a particular audience. They assume the reader is someone who wants to learn algorithmic coding interview problems, and has some basic programming experience. For an Easy problem, these may be the only prerequisites. For a Medium or Hard problem, the writer may assume knowledge of certain algorithms and data structures. Official LeetCode editorials for more advanced problems often link to a LeetCode Explore page and recommend that readers study before proceeding with the editorial.

When you write a model solution, the audience is much smaller. You can write it specifically for you. In fact, you can write it for you at a particular moment in your learning process. You can think of the model solution writing process as writing your own textbook. You are writing a textbook on LeetCode problems, a textbook that is customized for your unique background and skills and which you can update as these skills change and improve. The pages of your textbook are your model solutions, and the textbook represents everything you know about LeetCode problems.

Your textbook is organized around LeetCode problems because learning through problem-solving is the LeetCode philosophy. You choose the problems based on which concepts you need to learn and which problems are best suited to explore each concept. Taken as a whole, the problems in your textbook form a web of knowledge that covers the full set of LeetCode concepts that you have studied so far. The model solutions should reference each other so that as you learn one problem you also get better at related problems, even those that focus on different concepts.

Although problems are the organizing principle of your textbook, the fundamental concepts that you are learning are algorithms and data structures. The problems and the concepts mutually reinforce each other. As you learn your model solutions, you learn more about algorithms and data structures. And as you learn about algorithms and data structures, you get better at solving LeetCode problems. Although the goal is to show your problem-solving skills in an interview, the best way to remember how to solve these problems and apply the solutions to new problems is to understand every detail of how each problem works, both the theory behind the solution and the implementation details required to complete a working solution.

Like other types of textbooks, your LeetCode textbook will change over time. But unlike a textbook author, who might release a new edition every few years, you can update your textbook every day using that day’s practice session. Every repetition of a problem should point out a gap in your knowledge. (If it doesn’t, it means you’re done with a problem and you should remove it from your repetition queue). When you identify a gap, update your textbook with what you need to work on. And when you fill the gap through research and practice, update your textbook so you don’t forget your hard-won knowledge.

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.

LeetCode Tip 33: Prioritize Understanding

By Duncan Smith Leave a Comment Aug 23 0

LeetCode 2023

When you’re working on a challenging LeetCode problem, it’s easy to spend a lot of time on it. As long as you can keep thinking of ideas to try, you can keep adjusting your code to see if it improves your results. If your solution fails on large test cases, you can try to optimize it. If it passes most test cases, you can use the first failed case for testing. Hours can go by as you work on your last bug.

As I wrote in the previous tip, this isn’t a good use of study time. Rather than trying to fix every bug, it’s best to stop after an hour or two and look up what you’re missing. Then you can focus your efforts on learning that missing piece.

But while it’s good to minimize how much time you spend on a problem the first time around, multiple subsequent repetitions aren’t necessarily something to avoid. When you find a high-quality problem that covers topics you want to learn, there’s nothing wrong with putting time into it. The key is to spend time in the right areas.

When you’re repeating a problem for the second or third time, your goal should be the same as it was the first time around. Not to see if you can solve the problem with no help, but rather to figure out what skills this problem requires and make sure you learn them thoroughly. During subsequent repetitions, you have the advantage that you have already seen the solution once, so you are reinforcing skills rather than seeing them for the first time. But you can still get stuck. If that happens, consult your model solution to get back on track.

Since your model solution gives you a simple process to get unstuck, you don’t have to spend any more time than necessary when repeating a problem. But what isn’t as predictable is how long it will take you to completely learn a problem. You may find parts of the solution to be challenging to understand, even after you have solved the problem a few times. You may need to read up on math or algorithms. But understanding is the key to learning a solution for the long term. So it’s worth spending as much time as necessary to build this understanding.

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.

LeetCode Tip 32: Minimize the Time You Spend on Each Problem

By Duncan Smith Leave a Comment Aug 16 0

LeetCode 2023

When you’re working on a LeetCode problem, there is always a solution available, either the official solution or a solution from the discussion forums. So, as you’re working on your own solution, you always have the option of getting help. Your study strategy, therefore, needs to tell you how long to work on a problem before looking at a solution.

It’s easy to see why immediately looking at the solution is a bad idea. The only way to learn how to solve coding problems is by solving coding problems. Just reading about them doesn’t help. So we can rule out 0 minutes as the right amount of time to work on a problem.

Another approach is never looking at the solution until you have solved the problem on your own. If you can’t solve a problem, just move on to another one and return when you have more experience. This approach sounds reasonable. Every problem requires specific background knowledge, and until you have that background, you can’t make a proper attempt. So it’s better to save the problem for when you’re prepared for it.

The problem with this approach is the trial and error that it requires. Since you never look at the solution, you don’t know what skills you need to solve the problem, which means you don’t know when you’re ready to make another attempt. You could come back to the problem and find that you’re still missing a critical skill. If, rather than never looking at the solution, you plan to look at the solution only when you get stuck, you’re just moving the trial and error into the future. When you look at the solution, it will reveal what skills you need, but not which problems to use to practice them.

To get around these problems, you can use the techniques of problem lists and spaced repetition. Rather than approaching each problem as a test that tells you if you’re smart enough to solve it, look at it as a stepping stone to learn the next sub-skill on the way to mastering a topic.

Using this approach, you can minimize the time you spend on each problem. If you follow a problem list, you can solve problems in an efficient order, with each problem leading to the next one. If you get stuck on a problem after spending a reasonable amount of time (on the order of 1-2 hours at the most), read the solution to see what you’re missing. Then add the problem to your spaced repetition list and try it again in a day or two. Using this process, you can ensure that you’re always working on the right skills and solving problems at the right level.

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.

LeetCode Tip 31: Learn Problems and Sub-Problems

By Duncan Smith Leave a Comment Aug 9 0

LeetCode 2023

Consider this scenario: You’re repeating a problem. You skim the problem description and remember a good approach to use. You start implementing the solution, since you know the steps for the algorithm. Then at some point in the implementation, you get stuck. Maybe there’s some language syntax that you don’t use very often. Maybe you forget how to use a library function that you need. Maybe part of the solution just doesn’t click. Whatever the reason, your practice session grinds to a halt.

Since you have solved this problem before, there’s not much point in spending more time once you’ve made a reasonable effort. What you need is a targeted hint. A general web search might help, since it could get you unstuck without revealing more than you want about this problem. If that doesn’t work, you could consult your model solution, but you might see more than just the part you’re looking for.

One way or another, you get unstuck and continue your implementation, solving the problem and getting it accepted by the online judge. But now you need to figure out how to avoid getting stuck at the same point the next time around.

The simplest approach: just continue the spaced repetition process that you’re already using. You have the problem in your repetition queue. You practiced it, got stuck on part of it, got unstuck, and completed it. Now you can adjust the repetition interval as necessary. Next time you repeat the problem, you may remember what you need and avoid getting stuck again.

But this approach is inefficient. If you use a short repetition interval, you’ll be more likely to remember the part that tripped you up, but you’ll also have to implement the part that you already know. That won’t make the best use of your practice time. But if you use a longer repetition, you might get stuck on the same part again, which means you won’t progress on this problem.

A better approach is to use spaced repetition as a tool for learning both problems and sub-problems. Rather than just repeating full problems, you can also add sub-problems to your repetition queue. (A sub-problem is any part of a problem that you get stuck on). This will allow you to select the appropriate repetition interval at a more granular level.

One note about the sub-problem approach: LeetCode tools don’t directly support sub-problems. If you want LeetCode to test your solution completely, you have to submit an entire program. But there’s a way around this. Take your model solution code and delete the part that you want to practice, leaving the part that you already know. When you’re ready to practice a sub-problem, start by pasting the partial solution into the LeetCode editor. Then implement the missing part on your own. When you’re done with your practice, adjust your repetition interval per the usual process, based on how well you did on the sub-problem.

Once you start identifying sub-problems and practicing them independently of whole problems, you’ll reduce the time required to learn each problem, since your practice will be more targeted.

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.

  • « Previous Page
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • …
  • 9
  • Next Page »

Stay in the Know

I'm trying out the latest learning techniques on software development concepts, and writing about what works best. Sound interesting? Subscribe to my free newsletter to keep up to date. Learn More
Unsubscribing is easy, and I'll keep your email address private.

Getting Started

Are you new here? Check out my review posts for a tour of the archives:

  • 2023 in Review: 50 LeetCode Tips
  • 2022 in Review: Content Bots
  • 2021 in Review: Thoughts on Solving Programming Puzzles
  • Lessons from the 2020 LeetCode Monthly Challenges
  • 2019 in Review
  • Competitive Programming Frequently Asked Questions: 2018 In Review
  • What I Learned Working On Time Tortoise in 2017
  • 2016 in Review
  • 2015 in Review
  • 2015 Summer Review

Archives

Recent Posts

  • Do Coding Bots Mean the End of Coding Interviews? December 31, 2024
  • Another Project for 2024 May 8, 2024
  • Dynamic Programming Wrap-Up May 1, 2024
  • LeetCode 91: Decode Ways April 24, 2024
  • LeetCode 70: Climbing Stairs April 17, 2024
  • LeetCode 221: Maximal Square April 10, 2024
  • Using Dynamic Programming for Maximum Product Subarray April 3, 2024
  • LeetCode 62: Unique Paths March 27, 2024
  • LeetCode 416: Partition Equal Subset Sum March 20, 2024
  • LeetCode 1143: Longest Common Subsequence March 13, 2024
Red-Green-Code
  • Home
  • About
  • Contact
  • Project 462
  • CP FAQ
  • Newsletter
Copyright © 2025 Duncan Smith