How a lesson works
Every lesson has the same parts, in the same order. This page shows each part once, using a classic problem: Two Sum.
1. See the problem
Section titled “1. See the problem”You have a gift card worth exactly 50. The menu prices are 12, 35, 7, 15 and 44. Which two items use up the whole card?
Lessons start with an everyday problem like this, before any technical words.
2. Define the task
Section titled “2. Define the task”Now the exact version:
- Input: a list
numsand a numbertarget. - Output: the positions (indices) of the two numbers that add up to
target. - You can’t use the same item twice.
Example: nums = [12, 35, 7, 15, 44], target = 50 gives [1, 3], because 35 + 15 = 50.
3. Pause and think
Section titled “3. Pause and think”Try first, even if you get stuck. It helps you remember. Hints open one at a time:
Hint 1 of 3
Pick any price. Which exact price would its partner need to be?
Hint 2 of 3
For 35, the partner must be 50 − 35 = 15. So the question becomes: have I already seen 15?
Hint 3 of 3
"Have I seen this value before?" is a lookup. What data structure answers lookups quickly?
4. Understand the idea
Section titled “4. Understand the idea”Watch the idea run, one step at a time:
- 120
- 351
- 72
- 153
- 444
seen (value → index)
empty
Step 1 of 12. Start with an empty map. We want two numbers that add up to 50.
def two_sum(nums: list[ int], target: int) -> list[ int]: seen: dict[ int, int] = { } # value -> index for i, num in enumerate( nums): partner = target - num if partner in seen: return [ seen[ partner], i] seen[ num] = i return [ ]
The player stops to ask what happens next. Answer to test yourself, or press Just show me.
5. Climb the ladder
Section titled “5. Climb the ladder”Every problem has three solutions: Okay, Better and Best. Each one shows the idea, the code, its cost, and where it falls short. You can also see exactly what changed from the rung before.
Other lessons climb the same way with their own rungs. A system design case study grows a design from v1 to v3. Scenario and interview lessons show a weak, a decent and a strong answer.
Check every pair
The idea: Try each number with every number after it.
Most people write this first, and it works. The problem: with 100,000 numbers, that’s about 5 billion checks.
def two_sum(nums: list[int], target: int) -> list[int]: for i in range(len(nums)): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: return [i, j] return []- Time
O(n²)- Space
O(1)- Why
- Two nested loops over the list; nothing extra is stored.
Sort, then walk two pointers inward
The idea: Sort the values (keeping their original indices), then move one pointer from each end toward the middle.
Sum too small? Move the left pointer right. Too big? Move the right pointer left. The problem: sorting is extra work, and it’s easy to lose the original positions.
Highlighted lines are new in this rung.
def two_sum(nums: list[int], target: int) -> list[int]: pairs = sorted((value, i) for i, value in enumerate(nums)) lo, hi = 0, len(pairs) - 1 while lo < hi: total = pairs[lo][0] + pairs[hi][0] if total == target: return sorted([pairs[lo][1], pairs[hi][1]]) if total < target: lo += 1 else: hi -= 1 return []- Time
O(n log n)- Space
O(n)- Why
- Sorting dominates; the pairs list holds a copy of every value and index.
What changed from the previous rung
def two_sum(nums: list[int], target: int) -> list[int]: for i in range(len(nums)): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: return [i, j] pairs = sorted((value, i) for i, value in enumerate(nums)) lo, hi = 0, len(pairs) - 1 while lo < hi: total = pairs[lo][0] + pairs[hi][0] if total == target: return sorted([pairs[lo][1], pairs[hi][1]]) if total < target: lo += 1 else: hi -= 1 return []One pass with a hash map
The idea: Remember each number you've seen; for every new number, check whether its partner is already remembered.
When Best isn’t best: if the list is already sorted and memory is tight, use two pointers. That’s O(n) time and O(1) space.
Highlighted lines are new in this rung.
def two_sum(nums: list[int], target: int) -> list[int]: seen: dict[int, int] = {} # value -> index for i, num in enumerate(nums): partner = target - num if partner in seen: return [seen[partner], i] seen[num] = i return []- Time
O(n) on average- Space
O(n)- Why
- One loop, and each dictionary lookup is O(1) on average; the map can hold every number.
What changed from the previous rung
def two_sum(nums: list[int], target: int) -> list[int]: pairs = sorted((value, i) for i, value in enumerate(nums)) lo, hi = 0, len(pairs) - 1 while lo < hi: total = pairs[lo][0] + pairs[hi][0] if total == target: return sorted([pairs[lo][1], pairs[hi][1]]) if total < target: lo += 1 else: hi -= 1 seen: dict[int, int] = {} # value -> index for i, num in enumerate(nums): partner = target - num if partner in seen: return [seen[partner], i] seen[num] = i return []6. Write it yourself
Section titled “6. Write it yourself”Now write it in the page. Press Run tests to check your code. Python runs in your browser, so nothing is sent anywhere, and your code is saved here for next time.
Try the Okay version first. It passes the small tests but is too slow for the big extra one.
System design lessons call this part Draw it yourself: you build the design on a board instead of writing code.
Two Sum
Ctrl + Enter runs- Not run yet:
two_sum([2, 7, 11, 15], 9)→[0, 1] - Not run yet:
two_sum([3, 2, 4], 6)→[1, 2] - Not run yet:
two_sum([3, 3], 6)→[0, 1] - Not run yet: Extra test
- Not run yet: Extra test
- Not run yet: Extra test
Extra tests check tricky cases. You see their input only if they fail.
Try your own input
7. Compare
Section titled “7. Compare”| Rung | Time | Space | Work at n = 100,000 |
|---|---|---|---|
| Okay | O(n²) | O(1) | ≈ 5,000,000,000 pair checks |
| Better | O(n log n) | O(n) | ≈ 1,700,000 sort comparisons |
| Best | O(n) | O(n) | ≈ 100,000 lookups |
8. Common mistakes and edge cases
Section titled “8. Common mistakes and edge cases”9. Say it in the interview
Section titled “9. Say it in the interview”Each lesson ends with what to say out loud: questions to ask first, a 60-second walkthrough, and likely follow-ups. For Two Sum:
“The brute force checks every pair, which is O(n²). The waste is that each number already tells me its partner: target minus the number. If I keep the numbers I’ve seen in a hash map, each check is O(1). So one pass gives O(n) time and O(n) space. I’ll test
[3, 3]with target 6 to make sure I never reuse an element.”
10. Practice
Section titled “10. Practice”Three to five related problems, from easy to hard. Each has hints, an editor where code fits, and its own Okay, Better and Best answers. Some lessons use v1 to v3, or weak to strong, instead. Two Sum’s are a pair sum in a sorted list, counting pairs with a given difference, Three Sum and Four Sum.
11. Check yourself
Section titled “11. Check yourself”A few quick questions. Ones you miss are remembered, so you can see your weak spots later.
Flashcards
Section titled “Flashcards”Then add the lesson’s cards to your deck. Each card comes back a little later every time you get it right.
12. Cheat sheet
Section titled “12. Cheat sheet”Three to five points worth remembering next week. For Two Sum:
- Need a partner or complement → a hash map of seen values.
- Sorted input plus a pair condition → two pointers.
- Check before you insert.
13. Go deeper
Section titled “13. Go deeper”Links to the official docs and related lessons. Then Can you do these now? lists the lesson’s outcomes again, so you can tick off what you can do, and go back to anything you’re not sure of yet.
Mark the lesson complete when you finish. Already know it? Say so, and the roadmap shows it as known. It’s saved in this browser, and you can undo it.
Status: Not started
That’s every part. See them all in a real lesson: Two Sum, with practice problems. Or pick a track from the home page and start wherever suits you.