Whenever we are faced with a coding question, particularly in interviews, we sometimes don’t have a concrete plan as to how we are going to systematically answer the question. This may be due to nervousness, or just general unpreparedness to coding questions. According to the book Cracking The Coding Interview, there exists a six step process into how to solve coding questions. I am going to elaborate on that right now.
- Listen
Whenever we are doing a coding interview, the question usually won’t be given to you by text. It is usually communicated to you through voice. When the speaker is giving a description on the problem that you are going to have to solve, you need to pay very close attention to any information that is described. Assume that every detail is there for a reason, and write the information on the whiteboard. This is so that you can write an optimal algorithm as the answer to the question.
- Example
When you hear a question do not try and solve the question in your head. You should draw an example. We should try create an example that is:
- Specific: e.g. do not create an ideal BST (with two children). Use numbers and strings.
- Sufficiently large:
- Not a special case: Use general examples not corner cases.
- State a brute force
Once you have an example done you must state a brute force. You do this so that the interviewers don’t think that you are struggling to see even the easy solution. Then state the time and space complexity of it. This may not be the optimal solution, but it can help you understand the problem further and could be the starting point for your optimization.
- Optimize
After stating a brute force algorithm, you should try and optimize it. You can optimize it by:
- Looking for unused information
- Use a fresh example
- Solve it “incorrectly”
- Make time vs space tradeoff
- Precompute information
- Use a hash table
- Think about the best conceivable runtime
- Walk Through
When you have nailed down an optimal solution to the problem, don’t just dive into coding. When you code, you must write code that is nearly perfect, because testing and bug fixing take time. Understand completely the solution that you want to write, then write it.
- Implement
Once you understand the solution that you want to write, implement it. Start coding in the far left of the whiteboard, and avoid line slant. Write beautiful code. This means that your code must be:
- Modular
- Error Checks: Some interviewers care about this and some don’t. A good compromise will just be to write a todo first and explain what you are going to implement there.
- Use Other classes, structs when appropriate e.g. returning an array that is filled with the two lists that you need to return if you need to return two lists.
- Good variable names: Avoid using single letter variable names when possible. Use it only if you think the code will be too long, but don’t use it too much since it may confuse you.
If there is something that you can refactor, decide whether or not it’s worth it to refactor and state the reason why.
If you get confused, walk through the code again.
- Test
In real life you don’t submit code if you haven’t tested it. Here you need to do that as well. Use this approach whenever you want to test:
- Start with a conceptual test: Reading and analyzing what each code does, as if you are explaining it to a reviewer.
- Weird looking code: You may wrote that before for a good reason, but double check it again.
- Hot spots: These are some but not all cases where your code could cause problems
- Base cases in recursive code
- Null nodes in binary trees
- Start and end iteration of a linked list
- Small test cases
- Use small ones that can discover the same bugs as the large one.
- Special cases
- Test your code against null or single element values, the extreme cases or other special cases
If you found any bugs, you should always analyze why it occurred and make the best fix that you can make.
Some of the techniques that you can use to help solve the problems are:
Find the BUDs of your algorithm
- Bottlenecks: Part of the algorithm that slows down overall runtime.
- Unnecessary work: e.g. looping where you could’ve checked using a formula
- Duplicated work: Used multiple of the same iterations where you could’ve just used one.
Do it yourself: Usually when you are doing something in real life you intuitively are able to optimize it. Model the problem to real life situations and solve it.
Simplify and generalize: Make an easier version of the problem first, and then adapt it for the more complex version.
Base case and build: Solve problems from the base case first, and get to more complex/interesting cases later.
Data structure brainstorm: Find a data structure that can be applied to solve the problem.
Best conceivable runtime: The runtime that you are going to get if you input the best case input to an algorithm.