An Opinionated Guide to Leetcode
Motivations
If you're reading this, there's a high chance that I've recently did a mock interview with you. This guide covers my personal opinions on how to pass a leetcode style interview based on my experience as an interviewee and mock interviewer.
Format of the Interview
A technical interview will typically have the following stages:
- Introductions
- Leetcode Problems
- Opportunity To Ask Questions
Introductions
Both you and the interviewer(s) will go back and forth giving quick intros about yourselves. Its important to practice this as it sets the tone for the interview. I recommend writing down your intro as it will typically not vary much from between interviews.
The rough structure of my intro is below. You should try to match how in-depth you go based on how your interviewer does his intro.
- Name and current position
- Overview of recent experiences
- Overview of relevant project (if you have one)
- Mention of a hobby or 2 (what you do outside of work/school)
- What you're looking for (Why that company?)
Problems
The interview will then give you 1+ leetcode problem(s) to solve with your language of choice. New questions can expand on previous questions, requiring you to modify your old solution. You will likely solve the question on a platform such as Hackerrank or CodePair. Depending on the company, you may or may not have the ability to run code, use a debugger, or use Google. You'll also get varying amounts of information on the problem so its up to you to clarify.
I recommend structuring your approach to solving the problem using the GLUCK method (UMPIRE also works).
- G: Grasp understanding
- Ask questions about inputs/outputs
- Run through a simple case with the interviewer
- Jot down notes as comments
- L: Link to underlying patterns
- Identify the applicable patterns for the problem
- Mention your observations to the interviewer
- U: Unveil your plan
- Verbally go over your solution as a high level
- Verify that the approach matches input/output criteria
- C: Code and debug
- Start writing code based on your plan
- Talk through what you're doing with your interviewer
- K: Know your solution
- Go over Time Complexity
- Mention Bottle Necks
- Go Over Future improvements (if necessary)
Questions
Assuming you solve the above problems in a timely matter, you will have time at the end of the interview to ask questions about the company and your interviewer(s).
I recommend treating this section as an opportunity to gauge if the company is a good fit for you. This is typically the least practiced of all the sections despite it's importance as it will be used to help your interviewer(s) gauge your interest in the company, personality, and curiosity. I like to prepare a few questions about the company before the interview, as well as come up with a few questions specific for your interviewer based on their intro.
Here are some topics you can ask about. I try to make this more of a conversation by asking follow-ups.
- Interviewers background (if they didn't mention in intro)
- Follow up with technical questions if you have relevant experience
- What made them join the company?
- Day to day in the company
- I try to figure out if interviewer enjoys their day to day
- Company growth/future
- You probably don't want to join a struggling company
- Career growth/scope of role
- How long do promotions take
- What kind of work will you do as an engineer
Solving A Leetcode Problem
Solving a leetcode problem requires 4 things
- You need to know a programming language
- You need to know how to use key data structures for that language
- You need to have a toolbox of general purpose algorithms (ex. 14 common patterns)
- You need to know how efficient your algorithms are
Choosing a language
If you have an interview with short notice, I recommend using whatever language you're currently most comfortable with. Otherwise Python will make your life easier.
I recommend Python for the following reasons
- Your interviewer likely knows it
- If they don't its still easy to read
- Most data structures don't require imports
- Exceptions include heaps and the collections library (alternatives to the built-in data structures)
- Lots of tricks to keep your solutions simple and succinct (examples)
- My favorite trick is using
dict_obj.get(key, default_value)
to query a Python dictionary (Python's version of a hashmap)
- My favorite trick is using
Mastering Data Structures
A lot of leetcode problems involve changing the state of your input into a data structure. At a minimum, you should known the following (or their equivalents for a specific language). Mastering them is ideal but knowing the purpose and time complexity for each is usually enough.
- Array
- Queue
- Stack
- HashMap
- Heap/PriorityQueue
- Linked List/Tree/Graphs (They're all technically graphs)
Some others that are good to know about are:
I also recommend learning approaches to Concurrency for your language of choice.
Building your Algorithm Toolbox
Leetcode questions tend to fall into one of the following categories:
- Simple usage of a data structure
- Using a common pattern with minimal modification
- Using a pattern with modifications to fit the problem
- Using multiple patterns with modifications
To be able to solve problems efficiently, you should learn the following for at least the 14 patterns
- How the pattern works
- When to use the pattern (ex sorted array -> binary search)
- Bottle necks for the pattern
Use these resources to learn:
And these to practice
Evaluating Your Algorithms
After you solve a leetcode problem, you will likely to be asked to evaluate your solution in terms of time and memory complexity.
The trick for this section learn the time complexities of the previously mentioned patterns and other common operations (ex. popping from a queue) and combining them together. Also be sure to clearly define what the N in O(N) is referring to when describing your complexity.
Here are some tricks to know
- Sorting will take O(N*Log(N)) time
- An exception to this is Counting Sort being "linear" time
- N recursive calls will require O(N) memory at minimum due to the Call Stack
- Adding/Removing an item from a heap takes O(Log(N)) where N is the size of the heap
Debugging In Interviews
Often times the hardest part of solving a leetcode problem is fixing your mistakes in your solution on the spot. While the best way to avoid bugs is to take your time while coding, the time crunch of the interview can make you rush and overlook details. This section goes over some strategies to track down errors for different interview types.
Whiteboard/Text Editor
These types of interviews tend to focus more on your thought process over writing 100% correct code. However you may still have errors in your thought process and will need to identify where in your algorithm you went wrong.
The way I think about debugging is that you're trying the find the line of code doesn't do what you think it does. Sometimes you can track this down with just intuition, but its important to have an structured approach in case you don't.
What I like to do is to create a simple input and trace it's state in the code using comments. I usually use an input I went over with the interviewer at the start of the problem unless you have a specific input that you know fails. Below is an example with the Two-Sum Problem:
expected_output = [1,3]
def two_sum(nums, target):
seen = {}
for i, num in enumerate(nums):
complement = target - num
seen[num] = i
if complement in seen:
return [seen[complement], i]
return []
# Example trace
# nums=[1,2,3,4], target=6
# i=0: num=1, complement=5, seen={1:0}
# i=1: num=2, complement=4, seen={1:0, 2:1}
# i=2: num=3, complement=3, seen={1:0, 2:1, 3:2}
# complement is in seen, output is [2,2]
# Bug: value is added to seed before complement is checked in seen,
# allowing for an index to be returned twice
The level of details in the comments will vary based on the problem. For Two-Sum, I only tracked the state of the program at the if statement as the solution was relatively simple. For more complicated problems, you may want to track the state per line by putting comments next to each line of code instead.
Online IDE/Compiler
If your interviewer allows you to execute code during the interview, they likely value having a working solution more than the previous situation. Luckily, being able to run code gives you a few tricks that can help narrow down your bugs.
Tracing with print statements
This is essentially the same as the whiteboard strategy with print statements instead of comments. I recommend labeling the variables you're tracking in the below style.
print(f"Var1:{var1}, Var1:{var1})
Testing out small parts of the code
For more complicated problems, it may be tricky to find problems by tracing. One approach that may help is factoring out the different parts of your program into smaller functions and verifying that each of them work as intended. This is also called Unit Testing.
IDE with Debugger
If the interviewer gives you a debugger, its useful to know how to use one. All you need to do is set a breakpoint where you think the code is wrong, and step through the code.
Keep in mind the above approaches are still applicable and could be better choices depending on the situation.
Miscellaneous
Even if you master every aspect of acing a leetcode style technical interview, it may not be enough get you the job. The two things I recommend that can help you stand out as a candidate is honing your communication skills and learning about system design.
Communication
Being able to communicate clearly and confidently can make you a much stronger candidate. The best way to practice this is to do mock interviews with a wide variety of people. Also practice tailoring your communication style towards your specific interviewer. There is no one size fits all for communication.
System Design
Companies are hiring you for your ability to generate value for them, not based on your ability to solve leetcode problems. Understanding how the underlying tech works for large scale systems along with the tradeoffs of different technologies can come in handy when talking to your interviewer, even if the interview is not strictly a system design interview.
- Tech Interview Handbook
- In depth resource to technical and behavioral interviews
- Designing Data Intensive Systems
- Recommended for learning about system design
- System Design Primer and Hello Interview
- Useful for practicing system design interview questions
Other Tips
- Ensure you clarify any assumptions you're making
- "Can we assume the input fits in main memory?"
- Knowing the target time complexity for the solution can be a hint
- O(log(n)) is likely a form of binary search
- Listen for hints/suggestions from the interviewer
- They'll often nudge you towards the right data structure at least
- Name variables based on what they're used for
- Avoid overusing one letter names like "x" or "y"
- Be confident when asking/answering questions
- Interviewers may try to get you to second guess your answer even if it's optimal (the answer may just be incorrect so be careful)
Concluding Thoughts
While technical interviews can be intimidating, it's important to remember that you wouldn't have gotten an interview if the company didn't believe you can handle it. Interviews in general are expensive due to it requiring both recruiter and engineer time (the latter being especially pricy). Stay confident and you'll crush your interviews!