Back to Blog List

How to Leetcode Successfully

2025-03-23

Introduction

If you're in the software engineering profession and haven't lived under a rock for the past ten years, then you've most likely heard of Leetcode, the platform that lets you practice solving interview questions, primarily coding but more recently others as well. Leetcode has been a topic of much debate. Many argue that these algorithmic interview questions demonstrate a broken hiring system. They also say that practicing these questions is very time-consuming. Others counter that algorithmic coding questions don't require any prior technological knowledge aside from computer science fundamentals, making them good assessment tools for an engineer's technical abilities in a short (45-60 minute) interview window.

I’m not here to join that debate. Rather, I’d like to offer a different perspective on Leetcode and how I use it. I don’t see Leetcode as merely a tool for interview preparation. Instead, I view it as a way to sharpen my skills and become a better engineer. For the past couple of years, my approach to Leetcode has reflected this mindset. I don’t practice Leetcode only when I’m seeking a new job. Instead, I solve Leetcode questions regularly even when I’m happily employed. As a result, if or when I do need to interview, I don’t require weeks of practice. I’m always ready. I highly recommend adopting this mindset. But whether you’re practicing for interviews or, like me, simply trying to grow and improve, there are good ways to use Leetcode and bad ones. The focus of this post is on what I consider the best approach, yielding the highest return on your investment.

A motivating Example

Let’s start with a motivating example to illustrate the most fundamental principle of using Leetcode successfully. I’d also argue this principle applies to any domain you aim to learn and master.

Lets start with the following question from Leetcode: Partition Labels

Anyone who hasn’t been solving these types of questions on a regular basis will likely find it challenging, even seasoned Software Engineers.

The approach starts with noticing that each letter defines an interval with a start index and an end index: the start is the first occurrence of the letter, and the end is the last occurrence of that letter in the string. Our goal is to partition these intervals (for all letters) into non-intersecting intervals. Intervals that do intersect should be merged into a single partition.

If you made this observation and turned the problem into an interval problem, congratulations! you’re 80% done. However, the remaining 20% is critical: you need to turn your idea into code. Many candidates fail at this stage despite having strong intuition. The takeaway (and the principle leading from it) is that if I wake you up at 2:00 AM and ask you to write code to combine intersecting intervals, you should be able to do it easily. That way, in an interview, you spend more time on getting the “aha” moment - like reducing the string problem to an interval problem, and not worry that you won't have sufficient time for the coding. If you don’t know how to code the interval-combining portion by heart, you may lose too much time on that final 20% and miss your chance to ace the interview.

If you’re interested in my solution to the problem, it’s included at the end of this post.

Don't Memorize. Learn the Concepts and Patterns.

This principle follows from the example above. Most algorithmic problems, especially those on Leetcode, can be reduced to one or more common patterns. Truly unique problems that require novel approaches are the exception, not the rule. This means you should focus on understanding these patterns rather than solving questions randomly or, worse, trying to memorize solutions.

There are a few ways to identify patterns. The simplest and most direct is Leetcode Premium, which lists categories and relevant questions. Leetcode also has problem sets categorized and further limited in size (e.g., the Top 75 or Top 150) that are chosen by frequency. You can also check curated lists online, which tend to feature categories frequently seen in interviews. If you’re in a crunch, use those curated lists or the limited sets from Leetcode. If you have more time, tackle everything by category. The key is always to recognize, as you solve problems, which solution pattern each belongs to. By focusing on the underlying pattern, you build mental connections that help you when facing a completely new problem. Over time, your intuition will grow stronger, and you’ll often know by just looking at a problem which approach is best.

Many patterns recur in algorithmic problems, such as:

  • Graphs
  • Sliding Windows
  • Binary Search
  • Prefix Sums
  • Intervals
  • Disjoint sets
  • Greedy solutions to optimization problems
  • Dynamic Programming solutions to optimization problems

These higher-level categories can each be broken down into smaller sub-patterns that deserve separate study. If you’re studying only for interviews, you should focus on the most common ones. If you’re learning broadly, like I am, then once you’ve mastered the basics, move on to the ones that challenge you more.

How To Approach Each Pattern

As with anything, before you learn to run, you need to learn to walk. Start by solving the easy problems in a category. Don’t jump straight to the medium ones, let alone the hard ones. The easy questions will teach you the fundamentals that form the foundation for your technique and intuition. Once you can solve enough easy problems confidently, move on to the mediums. Still avoid the hards for now. At this point, you should be able to solve most medium problems in about 30 minutes. Don’t beat yourself up if you occasionally fail or exceed that time limit, whether on a medium or even an easy question. Consistency is the goal, not perfection.

Regardless of the problem difficulty, you should always spend at least 20 minutes trying to solve it on your own. With hard problems, it’s perfectly fine to code a brute-force solution that won’t pass all test cases because of time-limit issues. The key is to become comfortable with the struggle. That’s where growth happens. If you still have no idea after 20 minutes, read the editorial and check the highest upvoted solutions. In fact, even if you do solve a problem, it’s still worth reading those other solutions. You may discover more optimal approaches or useful tricks.

Finally, repetition is critical. Don’t just solve a problem once and forget it. Keep a sample of the problems you’ve solved, especially ones you struggled with. Revisit them from scratch periodically. This repeated exposure builds the mental pathways necessary for long-term memory. Think of it like muscle memory for coding.

Conclusion

Leetcode can be a powerful growth tool if you approach it the right way. Instead of viewing it only as interview prep, treat it as an opportunity to develop your problem solving skills and strengthen your grasp of fundamental algorithms and data structures. The key principles are:
1. Focus on learning patterns and concepts rather than memorizing solutions.
2. Start with easy problems to build a strong foundation before moving to medium and hard.
3. Give yourself time to struggle with a problem before checking solutions.
4. Study solutions even after you solve a problem to learn alternative ideas.
5. Practice consistently and repeatedly rather than cramming before interviews.
6. Build connections between problems by recognizing shared patterns.

By following these principles, you’ll be better prepared for technical interviews and become a stronger engineer overall. The patterns and problem solving strategies you learn will benefit you throughout your career: whether you’re designing systems, debugging complex issues, or optimizing performance. Remember: consistency and understanding beat memorization every time.

As promised, here is my solution to the Partition Labels problem:

class Solution {
public:
    vector<int> partitionLabels(string s) {

        using Interval = pair<int,int>;

        // Array of Intervals holding the first and last occurence of a letter
        vector<Interval> ints(26,{INT_MAX,INT_MIN});

        for(int i = 0; i < s.size(); ++i){
            auto &[start,end] = ints[s[i] - 'a'];
            start = min(start,i);
            end = max(end,i);
        }

        // Sort the Array of Intervals based on the first occurence of each letter
        sort(begin(ints),end(ints),[](const auto &p1, const auto &p2){
            return p1.first < p2.first || (p1.first == p2.first && p1.second < p2.second);
        });

        // Will hold  non-intersecting intervals
        vector<Interval> combined;

        Interval toPush(INT_MAX,INT_MAX);

        // Merge intersecting intervals 
        for(auto &p : ints){
            auto &[start,end] = p;
            auto &[s2,e2] = toPush;

            if(s2 == INT_MAX){
                s2 = start;
                e2 = end;
                continue;
            }

            if(start <= e2){
                s2 = min(s2,start);
                e2 = max(e2,end);
            }else{
                combined.push_back({s2,e2});
                s2 = start;
                e2 = end;
            }
        }

        if(toPush.first != INT_MAX){
            combined.push_back(toPush);
        }

        // Holds the result to return from the program
        vector<int> result;

        for(auto &p : combined){
            result.push_back(p.second - p.first + 1);
        }

        return result;
    }
};

Share this post