Lesson 01new · v2 format
Loops & Bounds: for and while Without Guessing
One rule decides every loop condition you will ever write: find the largest index the body touches.
01Why it matters & the intuition
You can understand bubble sort perfectly and still lose 20 minutes to IndexError: list index out of range. Almost every bug in early DSA code is a loop that starts one too early, stops one too late, or never stops.
This lesson turns loop bounds from guesswork into a rule, using the five loop shapes that make up nearly every algorithm in this course.
Think of it like…
Think of a row of seats numbered 0 to n − 1. Before you walk the row, ask one question: *which seat numbers will my hands touch?* If you look at your seat and the next one (i and i + 1), your last step must be at seat n − 2, or you'll reach for seat n, which doesn't exist.
02The code blocks
Each block: the template in Python and C++, a line-by-line trace (press play and watch the highlighted line and the variables), the mistakes that cost the most debugging time, and a recall drill. Do the drill without scrolling up— that’s what makes it stick.
Block 1
Visit every index
Use it for: Summing, printing, searching, building a new list — any time each element is handled once.
1 · The template
total = 0
for i in range(len(a)):
total += a[i]
print(total)2 · Watch it run, line by line
▶total = 02for i in range(len(a)):3total += a[i]4print(total)
Variables
- total
- 0
3 · Classic mistakes
for i in range(len(a) + 1):Reaches i = len(a), and a[len(a)] raises IndexError.for (int i = 0; i <= n; i++)C++ version of the same bug: `<=` runs one step too far and reads garbage memory silently.
4 · Recall it without looking
total = 0
for i in range():
total += a[i]Block 2
Compare neighbours (i, i + 1)
Use it for: Is it sorted? Count rises/drops, find the biggest jump, one pass of bubble sort.
1 · The template
rises = 0
for i in range(len(a) - 1):
if a[i + 1] > a[i]:
rises += 12 · Watch it run, line by line
▶rises = 02for i in range(len(a) - 1):3if a[i + 1] > a[i]:4rises += 1
Variables
- rises
- 0
3 · Classic mistakes
for i in range(len(a)): # then a[i + 1]On the last i, a[i + 1] is past the end → IndexError.for i in range(1, len(a)): # then a[i + 1]Shifting the start doesn't fix the end. If you start at 1, compare a[i - 1] with a[i] instead.
4 · Recall it without looking
for i in range():
if a[i + 1] > a[i]:
rises += 1Block 3
Every pair once (nested i < j)
Use it for: Brute force over pairs: two-sum by hand, closest pair, count inversions, selection sort's inner scan.
1 · The template
for i in range(len(a)):
for j in range(i + 1, len(a)):
print(a[i], a[j])2 · Watch it run, line by line
▶for i in range(len(a)):2for j in range(i + 1, len(a)):3print(a[i], a[j])
Variables
- i
- 0
3 · Classic mistakes
for j in range(len(a)):Visits (i, i) and both (1, 3) and (3, 1) — double counts.for j in range(i, len(a)):Includes j = i: every element gets paired with itself.
4 · Recall it without looking
for i in range(len(a)):
for j in range(, len(a)):
check(a[i], a[j])Block 4
Walk backwards
Use it for: Right-to-left scans: suffix sums, 'next greater' with a stack, removing items while iterating.
1 · The template
for i in range(len(a) - 1, -1, -1):
print(a[i])2 · Watch it run, line by line
▶for i in range(len(a) - 1, -1, -1):2print(a[i])
Variables
- i
- 2
3 · Classic mistakes
range(len(a) - 1, 0, -1)Stop is exclusive, so index 0 is skipped.for (size_t i = n - 1; i >= 0; i--)C++: an unsigned index is always ≥ 0, so this loops forever. Use int.
4 · Recall it without looking
for i in range(len(a) - 1, , -1):
print(a[i])Block 5
while with a bounds guard
Use it for: When the step isn't fixed: skip while a condition holds, two pointers, binary search, reading until a sentinel.
1 · The template
i = 0
while i < len(a) and a[i] == 0:
i += 1
print(i)2 · Watch it run, line by line
▶i = 02while i < len(a) and a[i] == 0:3i += 14print(i)
Variables
- i
- 0
3 · Classic mistakes
while a[i] == 0 and i < len(a):Checks a[i] before the bound — crashes when every element is 0. Guard first; `and` short-circuits.(no i += 1 in the body)The condition never changes → infinite loop. Every while body must move toward the exit.
4 · Recall it without looking
i = 0
while and a[i] == 0:
i += 103Key ideas
The one rule: largest index touched ≤ n − 1
Look at every a[...] in the loop body and find the biggest index expression. If it's a[i + 1], then i + 1 ≤ n − 1, so i ≤ n − 2, so the loop is range(n − 1) (C++: i < n - 1).
range(start, stop, step): stop is never included
range(5) → 0…4. range(2, 5) → 2, 3, 4. range(4, -1, -1) → 4…0. C++ writes the same thing as for (int i = start; i < stop; i += step).
for vs while
Use for when you know how many steps you take (each index once). Use while when the next step depends on data: two pointers moving at different speeds, binary search halving, skipping until something changes.
Every while needs three things
Setup before the loop (i = 0), a condition that eventually becomes false, and a move inside the body (i += 1). Missing the move is the #1 cause of infinite loops.
Guard first, then read
while i < n and a[i] == 0 is safe because and stops at the first false. Reversing the order reads a[n] and crashes.
04Cheat sheet
Every block from this lesson on one screen. Keep it open while you do the lab — then try the lab again without it.
# The five loop shapes — largest index touched decides the bound.
n = len(a)
for i in range(n): # every index: 0 .. n-1
...a[i]...
for i in range(n - 1): # neighbours: a[i], a[i + 1]
...a[i], a[i + 1]...
for i in range(n): # every pair once, i < j
for j in range(i + 1, n):
...a[i], a[j]...
for i in range(n - 1, -1, -1): # backwards: n-1 .. 0
...a[i]...
i = 0 # while: setup, guard-first condition, move
while i < n and a[i] == 0:
i += 105Complexity
| Loop shape | Body runs | |
|---|---|---|
| for i in range(n) | n times → O(n) | — |
| for i in range(n − 1) (pairs i, i+1) | n − 1 times → O(n) | — |
| for i … for j in range(i + 1, n) | n(n − 1)/2 times → O(n²) | — |
| for i … for j in range(n) | n² times → O(n²) | — |
| while i < n: i *= 2 (or halving) | ≈ log₂ n times → O(log n) | — |
06Check your understanding
07Code lab
Write Python(or switch to JavaScript) and run it against real test cases. Python runs inside your browser — nothing is uploaded — and a C++ reference solution is under “Solution”.
Count pairs that add to a target
Write a function that returns how many pairs of different positions (i < j) have a[i] + a[j] == target. Use the every-pair loop — the point is to get both bounds right on the first try.