Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

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.

  • Beginner
  • 20 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot

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

Six steps in order, from clarify to test. An example you cannot answer leads back to clarify; a failing case leads back to code.1. Clarifyinputs, outputs, sizesand special cases2. Examplesone normal and oneedge case, by hand3. Brute forcethe simplest correctmethod and its cost4. Optimisefind the repeatedwork and remove it5. Codesmall, named pieces,explained as you go6. Testtrace by hand, run theedge cases, state the costan example youcannot answera casefails

The six steps, and the two ways back

Text description of the diagram

The diagram shows six steps from top to bottom.

  1. Clarify the inputs, the outputs, the sizes and the special cases.
  2. Work one normal example and one edge case by hand.
  3. State the brute force: the simplest correct method, and what it costs.
  4. Optimise: find the work the brute force repeats and remove it.
  5. Code in small, named pieces, and explain them as you go.
  6. 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:

  1. 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?”)
  2. 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.”)
  3. 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²).”)
  4. 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.”)
  5. 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.”)
  6. 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? True if any two slots clash, otherwise False. 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:

Every pair of slots Python · slots/brute.py
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

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:

Sort, then compare neighbours

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

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:

The edge cases from step 1 Python · slots/edge_cases.py
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

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:

Written for distinct start times Python · slots/assumed_distinct.py
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

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:

A repeated key keeps only the last value Python · overwrite.py
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

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.

Python Online Compiler Change the slots in these examples and check your own answers from step 2. JavaScript & TypeScript Online Compiler Try the JavaScript version without the comparator to see how a string sort orders the slots.

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?

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.

  1. Question 1 of 5 Put the six steps in the order this lesson uses them.

    Give each item its position, from 1 (first).

    Show the answer to question 1

    Answer:

    1. Clarify the inputs, the output, the sizes and the special cases
    2. Work an ordinary example and an edge case by hand
    3. Say the brute force and what it costs
    4. Find the repeated work and remove it
    5. Write the code in small, named pieces
    6. Trace it, run the edge cases and state the cost

    Each step checks the one before it: examples test your understanding of the problem, the brute force tests the examples, the optimised plan keeps the brute force's answers, and the tests check the code.

  2. Question 2 of 5 For the delivery-slot problem, which questions can change the code you write?

    Choose every answer that is right.

    Show the answer to question 2

    Answer:

    • Can two slots start at the same minute?
    • How many slots can there be?
    • Does a slot that ends at 600 clash with one that starts at 600?

    The answer about a slot ending at 600 decides between < and <=, the one about equal start times rules out keeping one slot per start time, and the number of slots decides whether O(n²) is fast enough. Who built the dock and the language of the function's name would not change a line of the algorithm.

  3. Question 3 of 5 overwrite.py builds a dictionary from the slots (540, 600) and (540, 570) with {start: end for start, end in slots}. What does it print?

    What does this program print? Choose one answer.

    slots = [(540, 600), (540, 570)]
    end_at = {start: end for start, end in slots}
    print(end_at)
    Show the answer to question 3

    Answer: it prints

    {540: 570}

    A dictionary keeps one value per key, and a later value for the same key replaces the earlier one without any warning. That is how the version written for distinct start times loses a slot and misses the clash.

  4. Question 4 of 5 Why say the brute force before you optimise?

    Choose one answer.

    Show the answer to question 4

    Answer: If you have misread the problem, the interviewer can say so before you build on it, and you have a correct fallback

    The brute force shows what you think the problem is, so a misread surfaces early. It is also a correct solution to fall back on and a checker for the fast one. Saying it is not a delay: it earns credit for a working approach.

  5. Question 5 of 5 After sorting by start time, the code compares each slot's start with the previous slot's end. Slots that only touch must not clash. Which comparison means "clash"?

    Choose one answer.

    Show the answer to question 5

    Answer: start < previous_end

    The end of a slot is not part of it, so a slot starting at 600 after one ending at 600 is fine: 600 < 600 is false. With <= the touching slots would be reported as a clash, which the clarifying question ruled out.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.