Best Practices: Approaching Problems and Interviews

This chapter collects the habits that turn "I can code" into "I can solve problems under pressure": how to approach a new problem, common traps, and the interview loop.

The Universal Problem-Solving Loop

Every algorithmic problem, whether homework or a live interview, benefits from the same five steps.

1. Understand

Read the problem twice. State it back in your own words. Work a small example by hand.

Questions to ask:

  • What are the input types and constraints?
  • What's the expected output?
  • Are there edge cases (empty, negative, duplicates, one element)?
  • What's the scale? n = 10? 10^6? 10^9?

In an interview, ask clarifying questions out loud. "Can the input be empty?" "Are the numbers positive?" An interviewer who meant to specify may not have. And even if they did, asking shows you think about edge cases.

2. Plan

Before writing code, know the shape:

  • What data structure fits? (Array? Hash set? Tree? Graph?)
  • What pattern applies? (Two pointers? DP? BFS?)
  • What's the expected complexity?

If your first idea is O(n^2) and the input is n = 10^6, that's too slow. Think harder before you code.

Sketch the approach in 2 or 3 sentences. If you can't explain it, you can't code it.

3. Implement

Write the code. Keep it clean. Use good names. Comment the non-obvious.

Interview tip: talk through your code as you write. "I'm setting up a hash map to count occurrences. Then I iterate..." Makes you easier to hire; also, verbalizing catches bugs.

4. Review

Before claiming done, trace through an example by hand. Walk through the edge cases you identified in step 1.

Ask:

  • Does it handle empty input?
  • Does it handle a single element?
  • Does it handle duplicates? Negatives? The maximum?
  • What if the input is already sorted? Already reversed?

5. Evaluate

State the time and space complexity. Discuss trade-offs: could you use less memory? Could you handle streaming input? Could you parallelize?

Even if your solution is correct, discussing trade-offs shows mature engineering judgment.

Complexity Trade-offs

The levers you pull:

Time for Space

Use a hash set to achieve O(n) instead of O(n^2). Most "it used to be O(n^2), now it's O(n)" wins are this trade.

Space for Time

Pay more memory, run faster. Memoization, prefix sums, adjacency matrices (sometimes).

Preprocessing for Queries

Spend O(n log n) to sort, then answer each query in O(log n). Worth it when queries are frequent.

Worst Case vs Average Case

Quicksort is average O(n log n) but worst O(n^2). Merge sort is worst O(n log n). If you care about the tail, use merge sort.

Correctness vs Simplicity

Sometimes the O(n^2) solution is obviously correct and the O(n) is subtle. For non-hot-path code, the simple solution is the right one.

Reading Problems

Practice translating problem statements into algorithms.

  • "Find the minimum/maximum X" often means DP, greedy, or binary search on the answer.
  • "Find the number of ways to X" usually means DP or combinatorics.
  • "Is there a path/does X exist" often means BFS, DFS, or Union-Find.
  • "Contiguous subarray with X" often means sliding window or prefix sums.
  • "Pair/triple/quadruple sums to K" often means hash lookup or two pointers.
  • "Shortest path" with equal weights means BFS; with varied weights means Dijkstra.
  • "Longest X" often means DP.
  • "Scheduling/assignment/pairing" often means greedy.

Not universal rules. Patterns that help you narrow down.

Debugging Under Pressure

When your code fails a test case, don't panic.

  1. Re-read the failing case. What's special about it?
  2. Trace by hand. Walk through what your code does step by step.
  3. Print intermediate state. A well-placed print(state) often exposes the bug.
  4. Check boundaries. Off-by-one is the #1 interview bug. range(n) or range(n-1)? < or <=?
  5. Check edge cases. Empty, single-element, all-same-values.

Stay calm. Debug out loud in interviews; your interviewer wants to see how you think.

Interview-Specific Habits

Before the Interview

  • Review the 20 most common problem patterns (this book is a start).
  • Solve ~100 LeetCode problems spanning easy and medium.
  • Do 2 to 3 mock interviews with a friend or a service (interviewing.io, Pramp).
  • Practice coding on a whiteboard or shared editor, not your IDE.

During the Interview

  • Read the problem. Ask clarifying questions. Restate.
  • Propose an approach before coding. Get alignment.
  • Code the solution. Narrate.
  • Test with the given example. Test with edge cases.
  • State complexity. Discuss trade-offs.

Communication Tips

  • Think out loud. Silent solving is hard to evaluate.
  • Don't fight your interviewer. If they push on your approach, listen. They may see a cleaner way.
  • Admit when you're stuck. "I'm not sure what to try here, can I think out loud?" is much better than silent floundering.
  • Recognize your mistakes. "Oh, that's wrong. Let me fix it" beats defending a bad solution.

If You Don't Know the Answer

  • State what you'd do given more time.
  • Code a brute force first; say you'd optimize.
  • Discuss related problems you recognize.
  • Show you can reason about what's hard.

A partial answer delivered with strong communication beats a silent full answer every time.

Anti-Patterns

Memorizing Without Understanding

You learned the binary search template. You can recite it. Can you adapt it when the problem is a variant? Understanding beats memorizing.

Solving Without Thinking

Starting to code immediately to look productive. Almost always produces a mess that has to be rewritten. Plan first.

Premature Optimization

Writing O(n log n) when O(n^2) is fine for the given constraints. Know the constraints first, then pick the simplest solution that fits.

Confusing Brute Force with a Solution

"It works on small inputs" doesn't mean it works. If n = 10^6 and you're O(n^2), your solution won't finish. Know the scale.

Copy-Pasting Solutions Without Typing Them

The typing is where the learning happens. Shortcut that and you don't actually learn.

Skipping Edge Cases

"It works on the given example" isn't enough. Empty input, single element, all same, all distinct, the maximum, negatives. Test them.

Fighting the Library

Python's heapq, bisect, collections.Counter, sorted, sortedcontainers.SortedList exist. Know them. Reaching for a custom implementation is rarely the right move.

The One-Page Checklist

Before you call a problem done:

  1. You understand the problem in your own words.
  2. You've identified edge cases.
  3. You have a planned approach with stated complexity.
  4. Your code compiles / runs.
  5. You've tested the happy path.
  6. You've tested edge cases (empty, one, many, boundaries).
  7. You can state time and space complexity.
  8. You've considered an alternative approach and explained your choice.

Works for LeetCode. Works for interviews. Works for real engineering.

Where to Go From Here

You have the core toolkit: data structures, algorithms, patterns, problem-solving habits. The next level is depth and range:

  • Practice a lot. 200+ problems spanning all the topics in this tutorial.
  • Read CLRS (Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms) for the rigorous treatment.
  • Study real systems. How does SQLite implement B-trees? How does Redis implement sorted sets? How does git do diff? Every one is an algorithm applied to a domain.
  • Contribute to open source. Implementing algorithms in real code contexts teaches more than any textbook.
  • Teach someone. Explaining a sort algorithm to a beginner reveals which bits you actually understand.

Algorithms is a skill that compounds. Six months of consistent practice changes what problems feel tractable. The goal isn't to memorize every algorithm. The goal is to look at a new problem and recognize the shape.

That's where the practice takes you. Start now.