tl;dr: You’re doing it wrong. Use “The Six Steps” any time you practice LeetCode questions, preferably with another person. Keep an interview prep journal to track your progress.
The struggle is real
Have you been tackling LeetCode problems but don’t feel like you’re actually getting better in interviews? Feel like you’re able to come up with solutions but never make it to the optimal one? Do you keep running out of time before you can finish the question? Are you constantly having a tough time making it through difficult questions?
As a former Google software engineer and now career and interview coach, many of my clients have had this problem over the years. And if you were my client, I’d tell you that the problem isn’t that you’re not smart enough. Nor is it that you’re not working hard enough.
You’re probably practicing wrong.
Maybe you’ve been working through the Blind 75. Or maybe you’re just using example problems from Cracking the Coding Interview. You can pull questions from wherever you want, I don’t care. If you’re using the wrong strategy, it’s not gonna help you very much, no matter how many questions you practice.
Anyone who’s done weightlifting in the gym knows that good form and flexibility are essential. Those elements are necessary for getting the most out of your workout. Similarly, you need to practice good problem solving techniques when you’re doing LeetCode questions so that you’re maximizing the time you spend.
Keeping the real interview in mind
Many of you practice LeetCode problems incorrectly because you may be unfamiliar with how technical interviews work in real life. Let’s break that down real quick.
More than anything else, you need to understand that technical interviewing is a collaborative problem-solving exercise. The real interview won’t be you working through a LeetCode question by yourself. You’re going to be talking to a real human being. That’s important to state because, if you’re practicing by yourself, you probably aren’t thinking about the fact that you need to communicate out loud to another person and ask questions.
The biggest weakness with LeetCode problems is that there’s no human involved outside of yourself. The question needs to provide every detail necessary for you to solve the problem up front. In a real interview, your interviewer is probably going to hold back some information to see how you deal with ambiguity. In other words, they want to know if you’re going to ask questions or not.
Also, a LeetCode question can’t give you hints if you’re headed in the wrong direction. It can’t give you feedback on whether your approach is sound or not. All of this matters because your interviewer is going to assess how you handle feedback. If I, as your interviewer, ask you about whether you can optimize your solution, I’m going to expect something better than “I don’t know.”
Oh, and by the way, your interviewer isn’t obligated to provide you with examples or test cases. Some may do it to be helpful, and that’s great. But if you’re used to having all of that provided for you in your LeetCode questions, then you’re going to have a hard time in the real interview coming up with good examples and test cases.
A better way
With the necessary context established, let’s talk about how I think you should properly study example questions. The following strategy is what I used to ensure I got the most mileage out of every interview question. These steps will show how I applied my own problem solving framework (I call them “The Six Steps”) to practice interview questions.
1) Grab something to code on (not an IDE)
Before you begin, make sure that you are set up correctly for your mock interview. You may choose to write out code using a whiteboard, pencil and paper, or using a computer and a text editor or lightweight code editor like Notepad++ or TextPad. Try to avoid using a full-on IDE since they will often include features that you’re not likely to get in a real coding interview.
If you’re working with a mock interviewer, make sure to choose something that they’ll also be able to see and edit.
2) Decide whether to practice with a mock interviewer (strongly recommended) or by yourself
The best way to practice a LeetCode question is with another person who will assess you as an interviewer. Now this is going to come as a shock, but they don’t necessarily need to be a coder! Having a fellow software engineer or developer is ideal of course, but the important thing is finding someone that you can talk to and that can give you feedback. When studying for the Google interview, I relied on my wife who is definitely not technical but provided great feedback on my presence and demeanor.
Of course, there will be times when you will need to study alone and that’s fine. It will be essential for you to keep yourself accountable by not giving yourself more than a mock interviewer would in a real interview.
Oh, and whoever you work with, show them some thanks and appreciation. There’s always something you can do to compensate them for their time, whether with money, coffee, a public acknowledgement, or a gift card.
3) Start the clock
Most interviews are timed for 45-60 minutes. Decide what length of time you’re going to spend doing your mock interview based upon what you can expect for the real thing. Remember, you want to keep things as realistic as possible. Once your time is up, stop working. It’s important to train yourself to work well within time constraints.
One caveat here is that if you’re early into your interview prep journey, you may want to just benchmark yourself a few times to see how well you perform. You’ll still want to time yourself, but don’t stop by a specific time. Keep going until you reach an optimal solution or can’t think of anything else to do.
4) Articulate the problem
Your mock interviewer will begin by reading the question out loud. Under no circumstances during the whole interview are you to look at the sample question, examples, or constraints. Depend completely on your mock interviewer for any information you will need to solve the problem. This is why it is absolutely essential that you take notes as your interviewer is speaking so that you don’t have to ask them repeatedly for information they’ve already revealed.
To ensure you’ve heard the question correctly and that you’ve captured all the details, repeat the question back to your interviewer in your own words. If you’ve missed any details or added something unnecessary, your mock interviewer should let you know before proceeding.
If you’re practicing on your own, read the question out loud exactly one time along with the example. Once done, hide the question from view and then repeat the question in your own words as though you were actually talking to a real person. Take notes, but avoid writing the full problem statement out. Focus on short statements that help you remember the key parts of the problem you’re solving.
5) Ask questions and confirm assumptions
Here’s a handy tip for any interview. Always ask questions as the interviewee. Even if you think you know everything about how to solve a problem, and even if you’ve solved it before—still ask questions. Here are some questions you can ask for nearly any technical question:
- What’s the data type of the input and range of valid data?
- Does the input fit in memory?
- Can I expect to receive invalid data?
- How will input data be provided?
- Is the input data sorted already?
Remember, you should avoid asking questions that already have answers in the problem statement itself. It is important and encouraged, however, to re-state your assumptions in the hearing of your interviewer based on your understanding of the problem. For instance, if the problem mentions sorting whole numbers, you might assume that you’re going to be sorting 32-bit signed integers. State that assumption clearly or ask if that’s the case. The problem might actually require you to sort values that fit in a short integer, thus allowing you to save space (or a long 64-bit integer, which would obviously require more).
If you’re practicing alone or if you’re working with someone non-technical, you won’t have someone to answer your questions. Once you’ve documented all your questions and assumptions, you can peek at the LeetCode constraints at this point and write down the answers.
Again, try not to do a lot of writing. Prefer the least number of words needed to capture the essentials so you don’t have to keep details in your head.
6) Come up with example inputs and their outputs
At this point, you should begin working through examples. Two things are important at this step. One, look through any examples provided for you and reverse engineer them to figure out what details they reveal about the problem or its constraints. Two, add a couple of your own examples to demonstrate your understanding of those constraints or to prompt more questions. A way to think of examples is to treat them like test cases. Read up on test driven development (TDD) and unit testing as part of your interview prep if you habitually struggle to come up with good test cases.
A note to the mock interviewer. If the interviewee comes up with an example that violates constraints, let them know immediately. You are obligated to inform or remind them of the constraints in this case since this information may be critical to their being able to solve the problem.
Again, if you’re studying alone then you won’t have someone who can give you feedback on your examples. That’s OK. Still come up with examples and make best-guess assumptions to any unanswered questions you may generate at this step. LeetCode questions are generally specific enough that this shouldn’t be hard to do.
7) Brainstorm solutions and estimate their Big-O
You should now have enough information to start coming up with solutions to the problem. This is where the rubber meets the road. All of your studying of algorithms and data structures should amply prepare you for generating two or three viable ways to solve the problem. If you struggle a lot at this step, hit the books. You’ve got more learning to do.
The first step to good brainstorming is making a good guess about what the optimal solution looks like as expressed in Big-O. Is it possible that a constant time and constant space solution could exist? Why not? How about a logarithmic solution that ‘s O(lg n)? Remember, you’re always driving towards an optimal solution.
As an example, if I need to sort an array, I’m pretty sure that I can’t do any better than a O(n) time complexity and O(1) space complexity in the worst case. I claim linear time complexity because I’ll probably have to look at every element of input at least once, and I claim constant space because some sorts move elements in place so that I don’t need to use a separate data structure.
Once you think you know the ideal, the next step to good brainstorming is coming up with solutions and predicting their time and space complexity. Note that your first solution is probably going to be a brute force approach that is less than ideal. It also might also be the only solution you can come up with quickly. That’s OK! If that’s all you are able to come up with after a couple of minutes, just move on and go with it. But take the time to try to think about what you can do to get closer to the ideal, optimal solution.
A couple of principles can be helpful here:
- You usually have to use more space (memory) to make something go faster (and vice-versa), so if you’re optimizing for speed, you probably need to use a data structure.
- If your task is to build a new data structure, you are likely going to need to put two data structures together that complement each other. That means that in your studying, you should strive to know the strengths and weaknesses of each data structure and what problems they are designed to solve.
Again, take only short notes of your approach along with the expected time and space complexity. Do the estimation up front, even if you aren’t exactly sure! This can help you navigate your way through potential optimizations before you write the first line of code. You might find that a more optimal solution is just as easy, if not easier, to implement than the initial one you devised.
One last thing. You’re going to have to choose a solution to start implementing in the next step. It is OK for you to get input from your interviewer about which one they’d prefer to see you implement. It’s also fine for you to implement the easiest one first and then write an optimal version after that. Hopefully, you can find a great mock interviewer who will provide you with good guidance here.
8) Implement a solution
Now comes the easiest part of the experience. If you don’t find this to be the easiest, then you need to spend more time practicing writing out code. There’s a practical reason why I say this. You want to spend the least amount of time in this step so that you can devote more time to the others. It’s like the old proverb says: “measure twice, cut once.”
Because you are on an artificially constrained, time-boxed schedule, you should also avoid writing in pseudocode. In real life interviews, pseudocode generally doesn’t count. That means you’ll have to turn all your fake code into real code for it to be assessed properly. Instead of pseudocoding, I suggest you make small notes about your proposed algorithm instead of writing out fake code since that will probably be faster and provide the same utility.
Again, we’re talking about coding interviews primarily used by the big tech companies. If you can pseudocode on a take home assignment and make the deadline, go for it! But at the big tech level, I assure you that writing code will be the easiest part of the job. Understanding the work you need to do and vetting your design will probably be the bulk of what makes your job challenging.
- Say what you’re going to do before you write the code, then write the code. Don’t write code without explaining what you’re writing first. This ensures that the interviewer knows what you are thinking and has the opportunity to interrupt you if you’re going down the wrong path.
- Use good, clear variable names. Prefer to be verbose (e.g. indexCount instead of idxCnt). In a real interview, your interviewer won’t be the only one looking at your code. Readability matters a lot and is one of the reasons why code reviews are practiced in any major software shop.
- Don’t be afraid to use common APIs, syntactic sugar, and common idiomatic style for your language. If you have doubts, just ask your interviewer whether you can use something or not. And even more importantly, understand what is happening under the covers so that you’re aware of any performance penalty. For instance, calling the sort method on your collection costs between O(n lg n) or O(n2) depending on your language. Just calling that method won’t magically turn your code into a constant time solution.
- As part of your practice regimen, writing algorithms from memory can help you both get stronger in your programming language while also strengthening your comprehension of the algorithm itself
Remember, I strongly advise against using an IDE for coding up your solution to practice problems. And if you decide to use the LeetCode editor, don’t compile and run the tests. In most cases, you’re not actually going to get that in a real interview and even if you do, you will be tempted to use it as a crutch instead of properly walking through your code and testing it. Use a real compiler and unit tests after your time is up!
If you find yourself getting stuck during the implementation, use an example and work out the steps on paper to get from input to output. Try to visualize what you need to do on paper so that you can see what your code needs to do and then implement it.
9) Test your code
After you’ve typed the last semicolon or curly brace, you are still not done. You probably have at least one bug, typo, or missing variable that you need to fix. Walk through your code line by line, and check that each line is doing what you designed it to do. You should have a mental checklist of a few things to watch out for:
- Undeclared variables
- Off-by-one errors
- Reversed conditionals (e.g. “<” instead of “>=”)
- Bad variable names
- Null pointer exceptions
This is one of the big reasons why it’s essential to work out examples early in the problem solving process. You can easily use one of those examples to mentally run through the code and test that things work well, using paper and pencil or the text editor to track variable state.
If you haven’t reached an optimal solution yet, either go back to brainstorming or implement a more optimal solution you’ve already devised. Until the timer hits zero, you’ve still got more work to do. Keep going!
Wrapping up your mock interview
Regardless of whether you practice with a partner or on your own, make sure to document feedback on your performance. Keeping a journal or notebook with all your learnings is a very useful way of organizing tips, resources, and your own progress. I still have my mock interview journal that reflects just some of the work I poured into being successful.
If you worked with a mock interviewer, there are two questions you should answer: 1) how do you feel about your own performance, and 2) how would your interviewer rate you between hire, no hire, or on the fence. The key to these two questions is figuring out whether your self-assignment aligns with that of your interviewer. This will help you to shape the right expectations as you prepare so that you can build confidence and gain a good sense for yourself about whether you’re headed in the right direction.
Allow your interviewer space to critique you on non-technical things as well. Did you stumble a lot in your speech or use non-sequiturs? Were you making any annoying noises that should be avoided? Did you go quiet for long stretches of the interview? This feedback might not help you code better, but it will help you to communicate better and that’s half the challenge of doing well on technical interviews.
Lastly, now that your interview is over, feel free to copy your code into the LeetCode tool and make sure that it compiles and that the tests pass. If they don’t take note of any edge cases you missed. I’d actually recommend copying into an IDE so that you can get more detail about compilation errors or use a debugger to step through your code. Take note of failures or missed edge cases so that you can add them to your implementation checklist on the next coding question you tackle.
Want to succeed technical interviews and winning six-figure offers? Register for my free live webinar at morganlatimer.com/webinar. Learn the strategies I’ve used to help my clients land FAANG offers and beyond.