Coding Interview Patterns Module 1 – How to approach a coding round
A six-step method for any coding problem
Clarify, work examples, state a brute force, optimise, code and test, a routine for coding rounds followed through one problem with every output run.
What you will learn
- Apply clarify, examples, brute force, optimise, code and test in order
- Ask the clarifying questions that change the algorithm (sizes, duplicates, negatives, output format)
- Explain why stating a brute force first earns credit and prevents silent misreads
Before you start
On this page
Many failed coding rounds do not fail on the algorithm. They fail earlier, on a question that was never asked, or later, on a case that was never tried. The six steps below put the work in an order where each step protects the next: you check that you understood the problem before you plan, and you have a correct plan before you write fast code. This lesson follows the steps through one original problem, from a vague first statement to tested code in Python and JavaScript.
The six steps
The six steps, and the two ways back
Text description of the diagram
The diagram shows six steps from top to bottom.
- Clarify the inputs, the outputs, the sizes and the special cases.
- Work one normal example and one edge case by hand.
- State the brute force: the simplest correct method, and what it costs.
- Optimise: find the work the brute force repeats and remove it.
- Code in small, named pieces, and explain them as you go.
- Test: trace an example by hand, run the edge cases and state the time and space the code takes. When every case passes, you are done.
Two arrows lead back. From step 2 to step 1: an example whose answer you cannot work out means a question to ask. From step 6 to step 5: a case that fails sends you back to the code.
Each step, with something you might say aloud in brackets:
- Clarify the inputs, the output, the sizes and the special cases, until you could write the function’s signature and three tests. (“Can two slots start at the same minute?”)
- Work examples by hand, one ordinary case and one edge case, until you know the right answers without running anything. (“For these three slots I expect True, because the last two overlap.”)
- State the brute force: the simplest correct method and what it costs, until the interviewer agrees that it is correct. (“Comparing every pair works, but that is O(n²).”)
- Optimise: find the work the brute force repeats and remove it, then say the new cost and why the answer is still right. (“After sorting, only neighbours can clash.”)
- Code in small, named pieces, saying what each one is for, until it runs on your examples. (“This loop compares each slot with the one before it.”)
- Test: trace an example by hand, run the edge cases and state the time and space the code takes. (“Touching slots: 600 is not less than 600, so no clash.”)
The order is old advice. George Pólya’s How to Solve It, a classic book on mathematical problem solving, divides the work into four phases: understanding the problem, devising a plan, carrying it out and looking back. Steps 1 and 2 are the first phase, steps 3 and 4 the second, step 5 the third and step 6 the fourth. What a coding round adds is that a second person is listening, so every step is also something you say.
A worked problem: clashing delivery slots
The problem arrives as one vague sentence, which is normal:
The problem, as first stated
A warehouse has one loading dock, and trucks book delivery slots at it. Write a function that says whether any two slots clash.
Step 1: clarify
Each question below is worth asking because its answer changes the code. The answers are the interviewer’s, and what each one changes is in italics.
- How is a slot written? As
(start, end)in minutes after midnight, with the start before the end. This fixes the data you compare. - Does a slot ending at 600 clash with one starting at 600? No: the next truck can drive in as the last one
leaves. So compare with
<, not<=: the end is not part of the slot. - Can two slots start at the same minute? Yes, they are booked by different firms. So you cannot keep one slot per start time.
- Are the slots in time order? No, they are in booking order. So you have to sort, or compare every pair.
- How many slots can there be? Up to 100,000: the function checks a whole season of bookings. So O(n²) is too slow, and step 4 matters.
- What should it return?
Trueif any two slots clash, otherwiseFalse. No need to report which ones. - Can the list be empty? Yes, and then nothing clashes. One more test case.
Questions about the shape of the data (sizes, duplicates, order, negative values, empty input) and about the output format are the ones that change algorithms. A question whose answer would not change a line of code can wait.
Step 2: examples by hand
Write down a few slots and their answers before thinking about code. Here: (540, 600), (600, 660), (630, 700) clash,
because the last two overlap from 630 to 660; (540, 600), (600, 660) only touch, so they do not; and
(540, 600), (540, 570) clash, because they start together. If you cannot work out the answer to an example, you
have found another question for step 1.
Step 3: the brute force
Two slots (a, b) and (c, d) clash when each one starts before the other ends: a < d and c < b. Checking every
pair is short and plainly correct:
def clashes_brute(slots):
"""True if two delivery slots overlap: compare every pair, O(n^2)."""
for i in range(len(slots)):
for j in range(i + 1, len(slots)):
(a, b), (c, d) = slots[i], slots[j]
if a < d and c < b: # each one starts before the other ends
return True
return False
if __name__ == "__main__":
for slots in [
[(540, 600), (600, 660), (630, 700)],
[(540, 600), (540, 570)],
[(540, 600), (600, 660)],
]:
print(slots, clashes_brute(slots))
n = 100_000
print(f"Pairs to compare for {n:,} slots: {n * (n - 1) // 2:,}") Output
[(540, 600), (600, 660), (630, 700)] True [(540, 600), (540, 570)] True [(540, 600), (600, 660)] False Pairs to compare for 100,000 slots: 4,999,950,000
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
It gives the right answers for all three hand examples. The last line shows its problem: at the size from step 1, it would compare almost five billion pairs. Say that out loud. The brute force is not wasted time, for three reasons:
- It proves you read the problem correctly. If your brute force answers a different question, the interviewer can stop you now, before you spend twenty minutes making the wrong thing fast. A misread that is never said aloud only shows up at the end.
- It already earns credit. The rubric from the first lesson of this track counts explaining your plan under communication and moving from a working approach towards an optimal one under problem solving.
- It is your fallback and your checker. If time runs out, a correct brute force passes the small tests of an online assessment, and it can check the fast version (the brute-force-to-optimal lesson of this module shows how).
Step 4: optimise
Look at what the brute force repeats: it compares each slot with every other slot, although only slots that are near each other in time can clash. Sort the slots by start time and something stronger is true: if any two slots clash, then two neighbours in the sorted order clash. So one pass over neighbours is enough.
Why that holds: take any clashing pair, and call A the one that comes first in the sorted order. The slot right after A in that order starts no earlier than A and no later than A’s partner, so it starts before A ends. A also starts before that neighbour ends, because A starts no later than the neighbour, and every slot starts before it ends. So A and its neighbour clash. Sorting costs O(n log n) and the pass costs O(n), so the whole check is O(n log n) instead of O(n²).
Step 5: code
The same plan in Python and in JavaScript:
Python · slots/clash.py
from itertools import pairwise
def clashes(slots):
"""True if two delivery slots overlap. A slot is (start, end) in minutes, end not included."""
for (_, previous_end), (start, _) in pairwise(sorted(slots)): # sorted copy: O(n log n)
if start < previous_end: # slots that only touch (start == previous_end) do not clash
return True
return False
if __name__ == "__main__":
examples = {
"normal": [(540, 600), (600, 660), (630, 700)],
"same start": [(540, 600), (540, 570)],
"touching": [(540, 600), (600, 660)],
"booking order": [(700, 760), (540, 600), (590, 610)],
"no slots": [],
}
for name, slots in examples.items():
print(f"{name:14} {clashes(slots)}") Output
normal True same start True touching False booking order True no slots False
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 clash.py
JavaScript · slots/clash.mjs
// True if two delivery slots overlap. A slot is [start, end] in minutes; the end is not included.
export function clashes(slots) {
const ordered = [...slots].sort((a, b) => a[0] - b[0] || a[1] - b[1]); // a sorted copy: O(n log n)
for (let i = 1; i < ordered.length; i++) {
if (ordered[i][0] < ordered[i - 1][1]) return true; // slots that only touch do not clash
}
return false;
}
const examples = {
'normal': [[540, 600], [600, 660], [630, 700]],
'same start': [[540, 600], [540, 570]],
'touching': [[540, 600], [600, 660]],
'booking order': [[700, 760], [540, 600], [590, 610]],
'no slots': [],
};
for (const [name, slots] of Object.entries(examples)) {
console.log(`${name.padEnd(14)} ${clashes(slots)}`);
} Output
normal true same start true touching false booking order true no slots false
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node clash.mjs
Runs on this device, in your browser. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
Both sort a copy, so the caller’s list keeps its booking order: Python’s sorted() builds a new list, and in
JavaScript [...slots] copies the array before sort, which would otherwise reorder it in place. Python sorts
tuples by start, then by end, without being told; JavaScript needs the comparator, because its sort compares
elements as strings unless you pass one. pairwise, new in Python 3.10, yields each element with the next one.
Step 6: test
First trace one case by hand, saying the values aloud. For the touching slots: after sorting, the pairs are
(540, 600) then (600, 660); the loop compares 600 with the previous end, 600; 600 < 600 is false, so there is no
clash, and the function returns False. Then run the cases that the clarifying questions produced:
from clash import clashes
CASES = [ # (name, slots, expected): each one comes from a clarifying question or a hand example
("no slots", [], False),
("one slot", [(540, 600)], False),
("touching", [(540, 600), (600, 660)], False),
("same start", [(540, 600), (540, 570)], True),
("same slot twice", [(540, 600), (540, 600)], True),
("one inside another", [(540, 700), (560, 580)], True),
("booking order", [(700, 760), (540, 600), (590, 610)], True),
]
failed = 0
for name, slots, expected in CASES:
got = clashes(slots)
failed += got != expected
print(f"{'ok ' if got == expected else 'FAIL'} {name}: {got}")
print(f"{len(CASES) - failed} of {len(CASES)} cases pass") Output
ok no slots: False ok one slot: False ok touching: False ok same start: True ok same slot twice: True ok one inside another: True ok booking order: True 7 of 7 cases pass
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 edge_cases.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Every case traces back to a question or a hand example. Finish by stating the cost: O(n log n) time, and O(n) extra memory for the sorted copy. Mention the trade-off too: sorting in place would save that memory but change the caller’s list, which is a question to ask, not a decision to make silently.
The mistake that skipping step 1 invites
Suppose you skip the clarifying questions and quietly assume that no two slots start at the same minute. A natural design then keeps one end time per start time in a dictionary:
def clashes_distinct(slots):
"""Written as if no two slots could start at the same minute, which nobody promised."""
end_at = {start: end for start, end in slots} # a repeated start replaces the earlier slot
starts = sorted(end_at)
return any(later < end_at[earlier] for earlier, later in zip(starts, starts[1:]))
for name, slots, expected in [
("normal", [(540, 600), (600, 660), (630, 700)], True),
("touching", [(540, 600), (600, 660)], False),
("same start", [(540, 600), (540, 570)], True),
]:
got = clashes_distinct(slots)
print(f"{name:11} expected {expected!s:5} got {got!s:5} {'ok' if got == expected else 'WRONG'}") Output
normal expected True got True ok touching expected False got False ok same start expected True got False WRONG
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 assumed_distinct.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
It passes the ordinary example and the touching one, so it looks finished. It is wrong on the case the question would have produced, because a dictionary keeps one value per key:
slots = [(540, 600), (540, 570)]
end_at = {start: end for start, end in slots}
print(end_at) Output
{540: 570}
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 overwrite.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The slot (540, 600) is silently replaced by (540, 570), and the clash between them disappears with it. Nothing
crashes and no warning appears. Assumptions like this one fail without a sound, which is why step 1 asks about
duplicates every time.
Key takeaways
- Clarify, work examples, state a brute force, optimise, code, test: each step checks the one before it, and each is something to say aloud in a live round.
- The clarifying questions that matter are the ones whose answers change the code: sizes, duplicates, order, negative or empty input, and the output format.
- State the brute force and its cost before optimising: it exposes a misread early, earns credit, and becomes your fallback and your checker.
- Optimise by naming the repeated work. Here, sorting showed that only neighbours can clash, turning O(n²) into O(n log n).
- Test with the cases your questions produced, and finish with the time and space the code takes.
Exercise
Exercise · Easy · Python
List the free times at the loading dock
The warehouse from this lesson wants to show drivers when its loading dock is free. Write free_gaps(slots, opens, closes), which returns the free periods between opens and closes as a list of (start, end) pairs in time order.
Before you started, the interviewer answered your clarifying questions:
- How is a slot written? As (start, end) in minutes after midnight, with start < end. The end is not part of the slot. - Do all slots lie inside opening hours? Yes: opens <= start and end <= closes. - Can slots touch, overlap or repeat? All three, because they come from different bookings. - Are the slots in time order? No, they are in the order they were booked. - What about a period that is free for zero minutes? Leave it out: every gap you return has start < end. - May the function change the list it is given? No, the caller still needs it. - How many slots can there be? Up to 100,000.
free_gaps([(630, 660), (540, 600)], 480, 720) # [(480, 540), (600, 630), (660, 720)]
free_gaps([], 480, 720) # [(480, 720)]
Follow the six steps: work two more examples by hand from the answers above (one where slots overlap, one where they touch), say the brute force to yourself, then write the sorted version and run the sample tests.
Starter code · gaps.py
def free_gaps(slots, opens, closes):
"""Return the free periods between opens and closes as (start, end) pairs, in time order."""
# Replace this line with your code.
return [(opens, closes)] The sample tests · test_gaps.py
from gaps import free_gaps
def test_example():
"""the example from the prompt"""
assert free_gaps([(630, 660), (540, 600)], 480, 720) == [(480, 540), (600, 630), (660, 720)]
def test_no_slots():
"""with no slots the whole day is free"""
assert free_gaps([], 480, 720) == [(480, 720)]
def test_touching_slots():
"""slots that touch leave no gap between them"""
assert free_gaps([(480, 540), (540, 600)], 480, 600) == []
def test_overlapping_slots():
"""overlapping slots make one busy period"""
assert free_gaps([(540, 600), (580, 640)], 480, 720) == [(480, 540), (640, 720)]
def test_slot_inside_another():
"""a short slot inside a longer one does not end the busy period"""
assert free_gaps([(500, 600), (520, 540)], 480, 720) == [(480, 500), (600, 720)]
def test_repeated_slot():
"""the same slot booked twice"""
assert free_gaps([(540, 600), (540, 600)], 480, 720) == [(480, 540), (600, 720)]
def test_whole_day_booked():
"""a slot from opening to closing leaves nothing free"""
assert free_gaps([(480, 720)], 480, 720) == []
def test_slots_at_the_edges():
"""slots at opening and closing time leave only the middle"""
assert free_gaps([(660, 720), (480, 500)], 480, 720) == [(500, 660)]
def test_input_unchanged():
"""the caller's list is not changed"""
slots = [(630, 660), (540, 600)]
free_gaps(slots, 480, 720)
assert slots == [(630, 660), (540, 600)] A hint
Sort a copy of the slots by start time and walk through them once, keeping the time from which the dock is free. A slot that starts after that time leaves a gap before it. What should the free-from time become after a slot that lies entirely inside an earlier, longer one?
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Check yourself
5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
References
- How to Solve It: A New Aspect of Mathematical Method (Princeton University Press)
- Coding interview rubrics (Tech Interview Handbook)
- Sorting Techniques (Python Software Foundation)
- Value comparisons: sequences compare lexicographically (Python Software Foundation)
- itertools.pairwise() (Python Software Foundation)
- Time complexity of operations on built-in types (Python Software Foundation)
- Array.prototype.sort() (MDN Web Docs)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress