Free Handbook · Runs in your browser

Problem Solving

How to read a problem, break it into input → steps → output, test with small cases, and reason about Big-O in plain English — then the eight patterns that solve most interview and real-world problems, each with runnable code, and the graded practice problems that drill them.

0 / 108 lessons🔥 0 day streak
ShareXLinkedIn

Module 12 · what you'll be able to do

  • Turn a vague problem statement into a precise input/output contract before writing code
  • Test with the smallest cases first and find the edge cases on purpose
  • Estimate time complexity by reading the loops, and know which is fast enough
  • Recognise and apply the eight core patterns: two pointers, sliding window, hashmap, stack, recursion/backtracking, sorting, BFS/DFS, dynamic programming
  • Solve graded problems in /practice/python to prove it
01

How to read a problem

Most wrong answers come from solving the wrong problem. Before any code, write three lines: what comes in (type, size, can it be empty, can it have duplicates or negatives), what must come out (type, order, what if there is no answer), and one example you worked by hand. Interviewers grade this step; production bugs live in it.

  1. 1
    Restate it in one sentence

    "Given a list of daily prices, return the maximum profit from one buy followed by one sell." If you cannot say it, you cannot code it.

  2. 2
    Pin the contract

    Input: list of ints, length 0…10⁵, prices ≥ 0. Output: int ≥ 0; 0 if no profit possible. Empty list → 0.

  3. 3
    Work a tiny example by hand

    [7, 1, 5, 3, 6, 4] → buy at 1, sell at 6 → 5. Now you have a test.

  4. 4
    List the edge cases

    Empty; one price; always falling ([5, 4, 3] → 0); all equal; the best buy is the last day.

  5. 5
    Say the brute force out loud

    "Try every buy day and every later sell day" — O(n²). Correct first, fast second. Then ask what work is repeated.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
ok  (7, 1, 5, 3, 6, 4) → 5
ok  () → 0
ok  (5,) → 0
ok  (5, 4, 3) → 0
ok  (2, 2, 2) → 0
ok  (3, 1, 10) → 9
Your turn
Change the problem: you may buy and sell as many times as you like (but hold at most one share). Update the contract, the examples, then the code.
02

Big-O in plain English

Big-O says how the running time grows as the input grows. You do not need the maths — you need to read the loops. One loop over n items is O(n). A loop inside a loop is O(n²). Halving the problem each step (binary search) is O(log n). Sorting is O(n log n). A dict or set lookup is O(1). That is 90% of what you will ever use.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
has_duplicate_slow   slow (100s of ms)
has_duplicate_fast   fast (a few ms)
Your turn
Double data to 8,000 items. The slow one takes ~4× longer (n² → 4n²); the fast one ~2×. That is Big-O in one experiment.
Big-ONameFeels liken = 1,000,000
O(1)constantdict/set lookup, list index, appendinstant
O(log n)logarithmicbinary search, balanced trees~20 steps
O(n)linearone pass over the data1 million steps — fine
O(n log n)linearithmicsorting~20 million — fine
O(n²)quadraticnested loops over the same data10¹² — minutes to hours
O(2ⁿ)exponentialtrying every subset; naive recursionnever finishes past n ≈ 30
Quick check

Checking whether each of 50,000 emails is in a blocklist of 1,000,000 — which is the right structure for the blocklist?

03

Pattern 1 & 2: two pointers, sliding window

Two pointers: walk a sorted sequence from both ends (or two sequences in step) and move the pointer that helps. Turns O(n²) pair searches into O(n). Sliding window: keep a window [left, right] over a sequence, expand the right end, shrink the left when a condition breaks — for "longest/shortest subarray such that…" problems.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
(4, 6)
None
3 1 0
9
Your turn
Two pointers: remove duplicates from a sorted list in place (one pointer reads, one writes). Return the new length.
04

Pattern 3 & 4: hashmap, stack

Hashmap (a dict): trade memory for time. "Have I seen this before?", "what index did I see it at?", "how many of each?" — all O(1) per lookup. It is the answer to most "find the pair / count the things / group the things" problems. Stack (a list with append/pop): whenever the most recent unmatched thing is what you need next — brackets, undo, "next greater element", evaluating expressions.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
(0, 1) (0, 1)
[['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]
[('i', 4), ('s', 4)]
'([]{})'   True
'([)]'     False
'(('       False
''         True
[4, 2, 4, -1, -1]
05

Pattern 5 & 6: recursion / backtracking, sorting

Recursion fits problems that contain smaller copies of themselves: trees, nested data, "all combinations of". Backtracking is recursion that builds a partial answer, explores, and undoes — for permutations, subsets, puzzles. Sorting first often makes a hard problem easy: after sorted(), duplicates are adjacent, the two-pointer trick works, and "k-th largest" is an index. Python's sort is O(n log n) and stable, and key= makes it sort by anything.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
[[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]
[['a', 'b', 'c'], ['a', 'c', 'b'], ['b', 'a', 'c'], ['b', 'c', 'a'], ['c', 'a', 'b'], ['c', 'b', 'a']]
[(1, 4), (5, 8), (9, 10)]
[('ada', 36, 'data'), ('cy', 36, 'infra'), ('bob', 28, 'infra')]
('ada', 36, 'data')
Your turn
Write combinations(items, k) with backtracking — every subset of exactly k items — and check it against itertools.combinations.

Practice this — graded problems in your browser

06

Pattern 7 & 8: BFS / DFS, dynamic programming

BFS (breadth-first, with a queue) explores level by level — it finds the shortest path in an unweighted graph or grid. DFS (depth-first, with a stack or recursion) goes as deep as it can — for "is there any path", connected components, and tree traversals. Dynamic programming is recursion where sub-problems repeat, so you remember answers: @lru_cache turns an exponential recursion into a linear one, and a bottom-up table does the same without recursion.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
['A', 'B', 'D', 'F']
['C', 'D', 'E', 'F']
3
20365011074
6 -1
Your turn
Change bfs_shortest to return the number of edges instead of the path. Then make min_coins also return which coins it used.
How to spot DP
The problem asks for a min / max / count of ways, the answer for n depends on answers for smaller n, and the naive recursion recomputes the same calls. Write the recursion first, add @lru_cache, and only convert to a table if you need to.
07

Worked examples: from statement to solution

Two problems solved the way you would in an interview or a code review: contract, brute force, the pattern that improves it, tests. Read them, then do the practice set — twelve graded problems that use exactly these patterns on the kind of data engineering work that gets you hired.

pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
['ada']
[]
[]
pythonEdit it. ⌘/Ctrl + Enter runs.
You should see
ERROR [('db timeout', 2), ('disk full', 2)]
INFO [('started', 2), ('done', 1)]

Finish the Python handbook, then get hired

Sit the exam for your certificate, run your resume through the ATS checker, and see the jobs that ask for exactly this.

Check my resume
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.