Binary Search & Analyzing Loops

This deck traces binary search step by step on a concrete sorted array, gives the loop invariant that proves it correct with all three obligations spelled out, explains why halving the range gives logarithmic running time, and states the general rule for counting any loop's running time. It targets the off-by-one that never shrinks the range, trusting binary search on unsorted data, mixing up linear with logarithmic growth, and invariants that are stated but not actually preserved.

Subject: CS3000 Algorithms · 137 slides · symbolic lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Binary Search & Loop Analysis

Title

CS3000 Algorithms

How to search a sorted list in logarithmic time — and prove your loop actually works.

2. What you will be able to do

Objectives

Binary search is the first algorithm in CS3000 where you have to PROVE correctness, not just run it and hope. By the end you can:

  1. Trace binary search on a concrete sorted array, step by step, tracking lo, hi, and mid.
  2. State binary search's loop invariant in plain English and explain exactly what it promises.
  1. Prove all three obligations a loop invariant must satisfy: it holds initially, each iteration preserves it, and termination gives the right answer.
  2. Explain why halving the search range each step gives logarithmic running time.
  1. Count the running time of any loop using the rule 'work per iteration times number of iterations'.
  2. Handle the boundaries of lo, hi, and mid correctly, and spot the classic off-by-one bug before it bites you.

3. What survived from Proof by Contradiction?

Warm-up

Discussion prompt

Before we open Binary Search & Analyzing Loops: without looking back, what was the main idea of Proof by Contradiction, and what could you do by the end of it that you could not do before?

Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.

Answer:

How to start, run, and close a proof by contradiction: recognizing when it fits, negating a claim correctly (including flipping quantifiers), and reasoning to a genuine impossibility. Covers the irrationality of the square root of 2, the infinitude of primes, and a greedy exchange-argument sketch.

4. Toolkit check-in: name them before you look

Concept

Before any new material: cover the screen.

You have named 8 reusable moves so far. Say as many as you can out loud, by number, from memory.

Do not advance until you have actually tried. Getting four of eight is information; skipping the exercise is not.

Here they are. Score yourself.

Today adds 2 moves to this list. Everything else you will need is already above.

The question that starts every proof from here on is not how do I begin. It is which of these applies here?

5. Break it if you can: Toolkit check-in: name them before you look

Counterexample

Discussion prompt

You have named 8 reusable moves so far. Say as many as you can out loud, by number, from memory.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

Do not advance until you have actually tried. Getting four of eight is information; skipping the exercise is not.

6. Meeting Binary Search

Section

Section 1

7. The problem binary search solves

Concept

You have a sorted list of values and a target value. You want to find the target's position — or learn that it isn't there — using as few comparisons as possible.

binary search — An algorithm that finds a target value in a sorted array by repeatedly checking the middle of the remaining range and discarding the half that cannot possibly contain the target.

8. By analogy: The problem binary search solves

Analogy

Discussion prompt

Explain The problem binary search solves by analogy to something with no CS3000 Algorithms in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

You have a sorted list of values and a target value. You want to find the target's position — or learn that it isn't there — using as few comparisons as possible.

9. Each probe throws away half

Picture it

Animation

Shows: Each probe throws away half — a rendered Manim animation.

Rendered with Manim.

Takeaway: Seven elements, three probes. Doubling the array adds one probe.

10. Why binary search beats scanning one by one

Intuition

Checking every position left to right always works, but in the worst case it can take as many comparisons as there are items in the list.

Because the list is sorted, one comparison in the middle tells you which half the target could possibly be in. You throw away the other half for free, without ever looking at any of it.

11. Teach it back: Why binary search beats scanning one by one

Explain it

Discussion prompt

Explain Why binary search beats scanning one by one to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Checking every position left to right always works, but in the worst case it can take as many comparisons as there are items in the list.

12. Binary searching the answer itself

Picture it

Animation

Shows: Binary searching the answer itself — a rendered Manim animation.

Rendered with Manim.

Takeaway: One of the most reusable tricks in competitive work.

13. Binary search needs the array sorted

Concept

The whole trick depends on one fact: the array is arranged in increasing order. Comparing the target to the middle value only tells you which half to keep BECAUSE every value to its left is smaller and every value to its right is larger.

Lose that ordering, and the comparison tells you nothing reliable about where the target might be.

14. Sorted input is the entire precondition

Picture it

Animation

Shows: Sorted input is the entire precondition — a rendered Manim animation.

Rendered with Manim.

Takeaway: Sorting first costs n log n, so one search rarely justifies it.

15. What the array must support

Concept

Binary search also needs one more thing from the data structure: the ability to jump straight to any position — the first one, the middle one, the last one — in the same small amount of time, no matter how far it is from the start.

random access — The ability to read the value at any index directly, without walking through the elements before it. Arrays give you this for free; some other structures, like linked lists, do not.

16. Three variables that drive the search — lo, hi, mid

Concept

Binary search tracks three positions as it runs: lo, hi, and mid.

lo and hi — The lowest and highest index that might still contain the target. Together they describe the current search range.

mid — The index halfway between lo and hi, computed fresh each iteration. It is the one position binary search actually inspects each time.

17. Take the definitions apart: random access vs lo and hi

Definition probe

Sort into buckets

Every line below is part of the definition of random access or of lo and hi — one or the other, never both. Put each where it belongs.

random access
The ability to read the value at any index directly, without walking through the elements before it.; Arrays give you this for free; some other structures, like linked lists, do not.
lo and hi
The lowest and highest index that might still contain the target.; Together they describe the current search range.
b1
The ability to read the value at any index directly, without walking through the elements before it. Arrays give you this for free; some other structures, like linked lists, do not.
b2
The lowest and highest index that might still contain the target. Together they describe the current search range.

18. Why checking the middle is the smart move

Intuition

Checking the middle splits the remaining range into two pieces of roughly equal size. Whichever piece you keep, you've cut your remaining work in half.

Checking any other position still splits the range into two pieces — but not necessarily equal ones, so you would not be guaranteed to cut the work in half every single time.

19. Picture it first: The three variables as a shrinking window

Picture it

Figure (svg): A row of ten boxes numbered 0 through 9, with lo marking box 0, mid marking box 4, and hi marking box 9

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

Picture the array as a row of boxes. lo and hi are two pointers marking the left and right edges of the boxes still in play; mid sits between them.

20. The three variables as a shrinking window

Intuition

Picture the array as a row of boxes. lo and hi are two pointers marking the left and right edges of the boxes still in play; mid sits between them.

Figure (svg): A row of ten boxes numbered 0 through 9, with lo marking box 0, mid marking box 4, and hi marking box 9

Every iteration moves lo right or hi left, so the window between them can only shrink — never grow, and never stay the same size forever.

21. Plan first: Trace binary search: find 40

Step zero

Discussion prompt

Trace binary search: find 40 — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Initialize lo and hi to the array's endpoints

Answer:

  1. Initialize lo and hi to the array's endpoints
  2. Iteration 1: compute mid and compare
  3. Since A[mid] is too small, discard indices lo through mid
  4. Iteration 2: compute mid and compare
  5. Since A[mid] is too big, discard indices mid through hi
  6. Iteration 3: compute mid and compare
  7. Iteration 4: compute mid and compare
  8. Verify the full trace and the answer

22. Trace binary search: find 40

Worked example

Search this sorted array of 10 values, indices 0 through 9, for the target 40.

indexvalue
03
17
212
318
424
531
640
752
868
977

Initialize lo and hi to the array's endpoints

Why: The search range starts as the entire array — no positions have been ruled out yet.

\[ \text{lo}=0,\ \text{hi}=9 \]

Iteration 1: compute mid and compare

Why: mid is the floor of the average of lo and hi. A[mid] is 24, which is less than the target 40.

\[ \text{mid}=\left\lfloor\frac{0+9}{2}\right\rfloor=4,\quad A[4]=24 < 40 \]

Since A[mid] is too small, discard indices lo through mid

Why: Sortedness guarantees every value at or before mid is at most 24, so none of them can equal 40. Move lo just past mid.

\[ \text{lo}=\text{mid}+1=5 \]

Iteration 2: compute mid and compare

Why: A[mid] is now 52, which is greater than the target 40.

\[ \text{mid}=\left\lfloor\frac{5+9}{2}\right\rfloor=7,\quad A[7]=52 > 40 \]

Since A[mid] is too big, discard indices mid through hi

Why: Every value at or after mid is at least 52, so none of them can equal 40. Move hi just before mid.

\[ \text{hi}=\text{mid}-1=6 \]

Iteration 3: compute mid and compare

Why: A[mid] is 31, still less than the target 40.

\[ \text{mid}=\left\lfloor\frac{5+6}{2}\right\rfloor=5,\quad A[5]=31 < 40 \]

\[ \text{lo}=\text{mid}+1=6 \]

Iteration 4: compute mid and compare

Why: Now lo and hi have both landed on index 6, so mid is 6. A[mid] equals the target exactly.

\[ \text{mid}=\left\lfloor\frac{6+6}{2}\right\rfloor=6,\quad A[6]=40=40 \Rightarrow \text{found} \]

Verify the full trace and the answer

Why: Reading straight down the table, the search took 4 iterations and returned index 6, where A[6] is genuinely 40 — the direct lookup confirms the answer.

iterationlohimidA[mid]action
10942424 < 40, so lo = 5
25975252 > 40, so hi = 6
35653131 < 40, so lo = 6
46664040 = 40, found at index 6

23. Inclusive or exclusive, but pick one

Picture it

Animation

Shows: Inclusive or exclusive, but pick one — a rendered Manim animation.

Rendered with Manim.

Takeaway: Mixing the two conventions is where off-by-one bugs breed.

24. What "not found" looks like when the loop ends

Concept

Sometimes the loop runs out of range before finding the target. That happens when lo moves past hi — there is no room left to check.

When that happens, the array simply does not contain the target. The empty range isn't a bug — it's the loop correctly reporting failure.

25. What has to happen first: Trace binary search: search for 50 (not present)

Ranking

Put in order

Put the moves of Trace binary search: search for 50 (not present) into the order they have to happen.

  1. Run the same iterations as before
  2. Iteration 4 also fails to match
  3. lo is now greater than hi, so the loop stops
  4. Verify 50 truly is absent

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The first three iterations behave identically to the search for 40, since 50 is also greater than 24 and 31, and less than 52.

26. Trace binary search: search for 50 (not present)

Worked example

Search the same 10-value array for 50, a value that does not actually appear anywhere in it.

indexvalue
03
17
212
318
424
531
640
752
868
977

Run the same iterations as before

Why: The first three iterations behave identically to the search for 40, since 50 is also greater than 24 and 31, and less than 52.

iterationlohimidA[mid]action
10942424 < 50, so lo = 5
25975252 > 50, so hi = 6
35653131 < 50, so lo = 6

Iteration 4 also fails to match

Why: A[6] is 40, still less than 50, so lo moves one past hi.

\[ \text{mid}=6,\quad A[6]=40 < 50 \Rightarrow \text{lo}=7 \]

lo is now greater than hi, so the loop stops

Why: With lo = 7 and hi = 6, there is no valid index left between them. The range is empty.

\[ \text{lo}=7 > \text{hi}=6 \Rightarrow \text{report: not found} \]

Verify 50 truly is absent

Why: Scanning the full table of values — 3, 7, 12, 18, 24, 31, 40, 52, 68, 77 — confirms 50 never appears. The loop's conclusion matches reality.

27. Finding the FIRST match

Picture it

Animation

Shows: Finding the FIRST match — a rendered Manim animation.

Rendered with Manim.

Takeaway: The plain version returns any match; duplicates need this variant.

28. Something is wrong here: trusting binary search on unsorted data

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student runs binary search directly on this array, which is NOT sorted, searching for the target 3 (which is actually present, at index 3).

It is wrong. Say what breaks — and say it before you turn the page.

Correct: mid is 2 and A[mid] is 9. Since 3 is less than 9, the algorithm assumes the left side must hold it and discards the right — but nothing here is actually sorted, so that assumption is false.

Sort the array first. The same five values, in increasing order:

Why: mid is 2 and A[mid] is 9. Since 3 is less than 9, the algorithm assumes the left side must hold it and discards the right — but nothing here is actually sorted, so that assumption is false.

29. Trap: trusting binary search on unsorted data

Trap

The trap

A student runs binary search directly on this array, which is NOT sorted, searching for the target 3 (which is actually present, at index 3).

indexvalue
05
11
29
33
47

Compare against the middle and trust the sortedness assumption

Why: mid is 2 and A[mid] is 9. Since 3 is less than 9, the algorithm assumes the left side must hold it and discards the right — but nothing here is actually sorted, so that assumption is false.

\[ \text{mid}=2,\ A[2]=9,\ 3<9 \Rightarrow \text{hi}=1\ (\text{discards index 3, where the target actually is}) \]

Finish the (invalid) search

Why: The remaining checks at indices 0 and none left both fail, and the loop reports the target missing.

\[ \text{lo}=0,\text{hi}=1,\ \text{mid}=0,\ A[0]=5>3 \Rightarrow \text{hi}=-1 \Rightarrow \text{lo}>\text{hi}: \text{``not found''} \]

The real, wrong output: binary search reports 3 as missing, even though 3 is sitting right there at index 3 of this array.

The fix

Sort the array first. The same five values, in increasing order:

indexvalue
01
13
25
37
49

Now the middle comparison is trustworthy

Why: mid is 2 and A[mid] is 5. Since the array really is increasing, 3 being less than 5 genuinely means 3 (if present) is in the left half.

\[ \text{mid}=2,\ A[2]=5,\ 3<5 \Rightarrow \text{hi}=1 \]

Finish the search correctly

Why: One more comparison lands exactly on the target.

\[ \text{lo}=0,\text{hi}=1,\ \text{mid}=0,\ A[0]=1<3 \Rightarrow \text{lo}=1 \Rightarrow \text{mid}=1,\ A[1]=3=3: \text{found at index 1} \]

Sorting first (or only ever running binary search on data you already know is sorted) is what makes the middle comparison meaningful in the first place.

30. Watch it run: Trap: trusting binary search on unsorted data

Pattern

Step through it

Step through Trap: trusting binary search on unsorted data one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: index is 0
  2. Step 2: index is 1
  3. Step 3: index is 2
  4. Step 4: index is 3
  5. Step 5: index is 4

31. The Loop Invariant

Section

Section 2

32. What a loop invariant is, in plain English

Concept

A loop invariant is a claim about the program's variables that stays true every single time you check it — before the loop starts, after every iteration, and when the loop finally stops.

loop invariant — A statement that is true right before the loop begins, remains true after every iteration, and — combined with the reason the loop stopped — proves the algorithm's final answer is correct.

33. The search invariant, stated

Picture it

Animation

Shows: The search invariant, stated — a rendered Manim animation.

Rendered with Manim.

Takeaway: Termination plus the invariant gives correctness with no case analysis.

34. An invariant is a promise that must never break

Intuition

Think of the invariant as a promise you make at the very start: no matter how many times this loop spins, this one fact will still be true.

If even a single iteration could break that promise, the whole argument collapses — you would have no way to know what's true once the loop finally stops.

35. Binary search's invariant, stated precisely

Concept

Here is the exact promise binary search makes, and keeps, on every single iteration:

\[ \text{If the target is anywhere in the array, it lies at an index between } \text{lo} \text{ and } \text{hi}, \text{ inclusive.} \]

Notice what it does NOT say: it never claims the target IS in the array. It only claims WHERE the target would have to be, if it happens to be there at all.

36. Binary search in pseudocode

Concept

Three variables and one loop. The only thing the loop ever does is throw away the half of the range that cannot contain the target.

BINARY-SEARCH(A, target)
  lo = 1
  hi = A.length
  while lo <= hi
    mid = floor((lo + hi) / 2)
    if A[mid] == target
      return mid
    else if A[mid] < target
      lo = mid + 1
    else
      hi = mid - 1
  return NOT-FOUND

Lines 9 and 11 both step past mid rather than onto it. That plus one and that minus one are what guarantee the range strictly shrinks, and a range that strictly shrinks is a loop that must end.

37. Reading BINARY-SEARCH line by line

Notation

Every line of BINARY-SEARCH says one thing. Read the line, then read what it does — not the other way round.

Annotate

  • The range starts as the whole array. Both ends are inclusive, which is why hi is the last index and not one past it.
  • Less than or equal, not less than. With inclusive ends, a range holding exactly one item still has lo equal to hi and still needs checking.
  • Floor, so mid is always a real index. On an even-sized range this leans left, which is a choice, not a requirement.
  • The target is bigger, so everything from mid down is dead. Move lo past mid — not to mid, or the range may never shrink.
  • The mirror case. Move hi below mid for the same reason.
  • Reached only when lo passes hi, meaning the range is empty. An empty range is the honest answer that the item is absent.

38. Step BINARY-SEARCH yourself

Invariant

If the target is in the array at all, its index is between lo and hi. Check that sentence at every single step — it is true before the loop, every line preserves it, and at the end it is what makes NOT-FOUND trustworthy.

Step through it

At each step, name the half being discarded and say why it cannot hold the target.

  1. Line 2: lo at the first index
  2. Line 3: hi at the last
  3. Line 5: mid lands on 12
  4. Line 8: 12 is below 23, so look right
  5. Line 9: the left half is gone
  6. Line 5: mid lands on 23
  7. Line 6: found it
  8. Line 7: two comparisons for eight items

39. Every pass throws away half the range

Picture it

Animation

Shows: BINARY-SEARCH executing: the current line of pseudocode is highlighted while the data it touches changes.

Rendered with Manim.

Takeaway: Each pass discards half the remaining range, so a range of n collapses to one in about log n steps.

40. The invariant that makes it correct

Picture it

Animation

Shows: The invariant that makes it correct — a rendered Manim animation.

Rendered with Manim.

Takeaway: Correctness and termination, from one sentence.

41. Three obligations you must show about any invariant

Concept

Stating an invariant is easy. Proving it takes real work — and every proof of a loop invariant breaks into exactly three obligations.

  1. Initialization — show the invariant is true before the very first iteration runs.
  2. Maintenance — show that if the invariant is true before an iteration, it is still true right after it.
  3. Termination — show that when the loop stops, the invariant together with the reason it stopped gives you the answer you actually want.

42. The Loop Invariant skeleton

Concept

Every proof of this kind has the same five or six moves in the same order. The order is not something you rediscover each time.

It is on the right. It will stay on the right through the worked examples that follow.

Why this matters: the structure is now handled. You are not spending working memory on what comes next — you are spending all of it on the one hard step.

Steps 2, 3 and 4 are checkboxes. Step 1 is the whole proof — a badly stated invariant makes steps 2 through 5 impossible, and a well stated one makes them nearly automatic.

43. When nested loops really do multiply

Picture it

Animation

Shows: When nested loops really do multiply — a rendered Manim animation.

Rendered with Manim.

Takeaway: The distinction that separates n squared from n plus E.

44. Obligation 1: Initialization

Concept

Initialization asks one question: before the loop runs even once, is the invariant already true?

For binary search, lo and hi start at the two ends of the whole array. So the range the invariant talks about starts out as the entire array.

45. Guess the shape of the answer: Verify initialization on our example array

Estimation

Predict first

Use the same 10-value array from before.

Commit before you compute: what does Verify initialization on our example array come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Verify the invariant holds before the loop runs

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. If the target is anywhere in the array at all, it is certainly within indices 0 through 9, since that is every index there is.

46. Verify initialization on our example array

Worked example

Use the same 10-value array from before.

indexvalue
03
17
212
318
424
531
640
752
868
977

Set lo and hi to the array's endpoints

Why: lo starts at the first index, hi at the last — the array has 10 elements, indices 0 through 9.

\[ n=10,\quad \text{lo}=0,\quad \text{hi}=n-1=9 \]

Note exactly what range lo through hi covers

Why: With lo at 0 and hi at 9, the range spans every single index the array has.

\[ \text{lo}..\text{hi} = 0..9 = \text{the entire array} \]

Verify the invariant holds before the loop runs

Why: If the target is anywhere in the array at all, it is certainly within indices 0 through 9, since that is every index there is. Initialization holds — trivially, but genuinely.

47. Verify initialization on our example array — line by line

Picture it

Animation

Shows: Each line of the worked example "Verify initialization on our example array", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: If the target is anywhere in the array at all, it is certainly within indices 0 through 9, since that is every index there is. Initialization holds — trivially, but genuinely.

48. Obligation 2: Maintenance

Concept

Maintenance asks: assuming the invariant was true right before this iteration, is it still true after the iteration updates lo or hi?

Binary search's loop body only ever does one of two things: move lo up past mid, or move hi down past mid. Each one needs its own justification.

49. Why comparing against A[mid] lets you safely discard half

Intuition

Because the array is sorted, comparing the target to the middle value tells you something about EVERY position on one side, not just the middle one.

If the middle value is smaller than the target, every value to its left is even smaller still — none of them can be the target. The entire left half, including the middle itself, is safe to throw away.

50. Decision point: you are allowed to throw away half the array

Intuition

What move should we make next?

The value at the midpoint is smaller than the target, and the array is sorted:

\[ A[\text{mid}] < \text{target}, \qquad A[0] \le A[1] \le \cdots \le A[n-1] \]

Everyone agrees the left half can go. The question is what licenses that.

Name the move, then name the exact sentence that has to stay true for the discard to be legal.

_Look at your toolkit. Say a move number out loud before this slide advances._ A wrong guess is useful. A silent guess is not.

51. What has to be given first: Verify maintenance: A[mid] smaller than the…

Missing information

Discussion prompt

Pick up the search for 40 at its very first iteration, where the invariant (assumed true) says: if 40 is in the array, it's within lo through hi.

What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.

Hint: Anything you would have to invent to get started is a thing the problem must supply.

Answer:

mid lands on index 4, and A[4] is 24 — smaller than the target 40.

52. Verify maintenance: A[mid] smaller than the target

Worked example

Pick up the search for 40 at its very first iteration, where the invariant (assumed true) says: if 40 is in the array, it's within lo through hi.

\[ \text{Before: lo}=0,\ \text{hi}=9\quad(\text{invariant assumed true here}) \]

Compute mid and compare to the target

Why: mid lands on index 4, and A[4] is 24 — smaller than the target 40.

\[ \text{mid}=4,\quad A[4]=24 < 40 \]

Rule out every index from lo through mid

Why: Sortedness means indices 0 through 4 hold values at most 24, all strictly less than 40. None of them can equal the target, so the target — if present — cannot be there.

\[ A[0..4] \le 24 < 40 \Rightarrow \text{target} \notin A[0..4] \]

Update lo to mid plus one

Why: Having proven indices 0 through 4 are safe to exclude, the new range starts right after mid.

\[ \text{lo}=\text{mid}+1=5 \]

Verify the invariant still holds after the update

Why: Since 0 through 4 are proven not to hold the target, if the target is in the array at all, it must now be within indices 5 through 9 — exactly the new lo through hi. (And indeed the real target 40 sits at index 6, safely inside 5..9.)

53. Verify maintenance: A[mid] smaller than the target — line by line

Picture it

Animation

Shows: Each line of the worked example "Verify maintenance: A[mid] smaller than the target", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: Since 0 through 4 are proven not to hold the target, if the target is in the array at all, it must now be within indices 5 through 9 — exactly the new lo through hi. (And indeed the real target 40 sits at index 6, safely inside 5..9.)

54. Plan first: Verify maintenance: A[mid] bigger than the target

Step zero

Discussion prompt

Verify maintenance: A[mid] bigger than the target — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Compute mid and compare to the target

Answer:

  1. Compute mid and compare to the target
  2. Rule out every index from mid through hi
  3. Update hi to mid minus one
  4. Verify the invariant still holds after the update

55. Verify maintenance: A[mid] bigger than the target

Worked example

Continue to the search's second iteration, where the invariant now says: if 40 is in the array, it's within lo=5 through hi=9.

\[ \text{Before: lo}=5,\ \text{hi}=9\quad(\text{invariant assumed true here}) \]

Compute mid and compare to the target

Why: mid lands on index 7, and A[7] is 52 — bigger than the target 40.

\[ \text{mid}=7,\quad A[7]=52 > 40 \]

Rule out every index from mid through hi

Why: Sortedness means indices 7 through 9 hold values at least 52, all strictly greater than 40. None of them can equal the target.

\[ A[7..9] \ge 52 > 40 \Rightarrow \text{target} \notin A[7..9] \]

Update hi to mid minus one

Why: Having proven indices 7 through 9 are safe to exclude, the new range ends right before mid.

\[ \text{hi}=\text{mid}-1=6 \]

Verify the invariant still holds after the update

Why: Since 7 through 9 are proven not to hold the target, if the target is in the array at all, it must now be within indices 5 through 6 — exactly the new lo through hi. (Target 40 at index 6 is indeed still inside 5..6.)

56. Verify maintenance: A[mid] bigger than the target — line by line

Picture it

Animation

Shows: Each line of the worked example "Verify maintenance: A[mid] bigger than the target", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: Since 7 through 9 are proven not to hold the target, if the target is in the array at all, it must now be within indices 5 through 6 — exactly the new lo through hi. (Target 40 at index 6 is indeed still inside 5..6.)

57. The move we just made, named

Concept

The move: #9 (Loop invariant).

State the invariant first, then the code

Why: If the target is anywhere in the array, it is inside the window from lo to hi. That one sentence is what makes discarding half legal — not the sortedness by itself.

\[ \text{target} \in A \;\Longrightarrow\; \text{target} \in A[\text{lo} \ldots \text{hi}] \]

Every branch is now a maintenance check

Why: Each of the three cases has one job: show the sentence is still true afterwards. You are no longer reasoning about the algorithm — you are checking one sentence, three times.

This is why the invariant is stated before the trace and not derived from it. Written first, it tells you what to check. Written afterwards, it is just a summary.

58. Decode the notation: The move we just made, named

Notation

Annotate

From The move we just made, named — read this one piece at a time. What is each part doing?

On: \( \text{target} \in A \;\Longrightarrow\; \text{target} \in A[\text{lo} \ldots \text{hi}] \)

  • If the target is anywhere in the array, it is inside the window from lo to hi. That one sentence is what makes discarding half legal — not the sortedness by itself.
  • Each of the three cases has one job: show the sentence is still true afterwards. You are no longer reasoning about the algorithm — you are checking one sentence, three times.

59. Obligation 3: Termination

Concept

Termination asks: when the loop finally stops, does the invariant — combined with WHY it stopped — actually hand you the correct final answer?

Binary search's loop stops for exactly one of two reasons: it finds the target at mid, or the range becomes empty because lo has moved past hi.

60. Guess the shape of the answer: Verify termination: the found case

Estimation

Predict first

The search for 40 reaches its fourth iteration with lo and hi both at index 6.

Commit before you compute: what does Verify termination: the found case come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Verify the returned index is correct

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. A[6] literally equals 40 by direct lookup in the table — the loop's found-case termination is trivially correct, since it only ever returns after an exact match.

61. Verify termination: the found case

Worked example

The search for 40 reaches its fourth iteration with lo and hi both at index 6.

\[ \text{lo}=6,\ \text{hi}=6 \]

Compute mid and compare

Why: With lo and hi equal, mid is forced to also be 6.

\[ \text{mid}=6,\quad A[6]=40 \]

A[mid] equals the target, so return mid

Why: The loop's stopping reason here is a direct match — there is nothing left to infer from the invariant, because the comparison already handed us certainty.

\[ A[6]=40=\text{target} \Rightarrow \text{return } 6 \]

Verify the returned index is correct

Why: A[6] literally equals 40 by direct lookup in the table — the loop's found-case termination is trivially correct, since it only ever returns after an exact match.

62. Verify termination: the found case — line by line

Picture it

Animation

Shows: Each line of the worked example "Verify termination: the found case", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: A[6] literally equals 40 by direct lookup in the table — the loop's found-case termination is trivially correct, since it only ever returns after an exact match.

63. What has to happen first: Verify termination: the not-found case

Ranking

Put in order

Put the moves of Verify termination: the not-found case into the order they have to happen.

  1. Notice lo is now greater than hi
  2. Apply the invariant to the now-empty range
  3. Verify by scanning the array directly

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. There is no index that is at once at least 7 and at most 6 — the range described by lo through hi is empty.

64. Verify termination: the not-found case

Worked example

The search for 50 (which is not in the array) reaches a point where lo has moved past hi.

\[ \text{lo}=7,\ \text{hi}=6 \]

Notice lo is now greater than hi

Why: There is no index that is at once at least 7 and at most 6 — the range described by lo through hi is empty.

\[ \text{lo}=7 > \text{hi}=6 \Rightarrow \text{lo}..\text{hi} = \varnothing \]

Apply the invariant to the now-empty range

Why: The invariant says: IF the target is in the array, it lies between lo and hi. But there is no index between 7 and 6 at all. The only way that statement can still be true is if its condition is false — the target is simply not in the array.

\[ (\text{target}\in A \Rightarrow \text{target}\in\varnothing) \Rightarrow \text{target}\notin A \]

Verify by scanning the array directly

Why: The full array — 3, 7, 12, 18, 24, 31, 40, 52, 68, 77 — never contains 50. The loop's logical conclusion matches a plain, direct check.

65. Verify termination: the not-found case — line by line

Picture it

Animation

Shows: Each line of the worked example "Verify termination: the not-found case", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: The full array — 3, 7, 12, 18, 24, 31, 40, 52, 68, 77 — never contains 50. The loop's logical conclusion matches a plain, direct check.

66. Something is wrong here: an invariant stated but not actually preserved

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student states the correct invariant, but writes the loop body's maintenance step backwards: whenever A[mid] is less than the target, they shrink from the top instead of moving lo up.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: mid is 4 and A[4] is 24, which is less than 40.

The correct rule moves lo up when A[mid] is too small, discarding the half that's proven too small and keeping the half that might still hold the target.

Why: mid is 4 and A[4] is 24, which is less than 40. The buggy rule shrinks from the top instead of the bottom.

67. Trap: an invariant stated but not actually preserved

Trap

The trap

A student states the correct invariant, but writes the loop body's maintenance step backwards: whenever A[mid] is less than the target, they shrink from the top instead of moving lo up.

\[ \text{Buggy rule: if } A[\text{mid}] < \text{target}: \ \text{hi} = \text{mid} - 1 \]

Trace it on the search for 40, iteration 1

Why: mid is 4 and A[4] is 24, which is less than 40. The buggy rule shrinks from the top instead of the bottom.

\[ \text{mid}=4,\ A[4]=24<40 \Rightarrow \text{hi}=\text{mid}-1=3\ (\text{should have been lo}=5) \]

The invariant is now false

Why: The target 40 actually lives at index 6, but the new range is 0 through 3 — index 6 isn't in it. The claim 'if 40 is in the array, it's between lo and hi' just failed, one iteration in.

\[ \text{target index} = 6 \notin \text{lo}..\text{hi} = 0..3 \]

The buggy search runs to its wrong conclusion

Why: Continuing the same backwards rule shrinks the range down to nothing, and the loop reports 40 as missing — even though it is sitting right there at index 6.

\[ \text{lo}=0,\text{hi}=0 \to \text{hi}=-1 \Rightarrow \text{``not found''}\ (\text{wrong: 40 is at index 6}) \]

The fix

The correct rule moves lo up when A[mid] is too small, discarding the half that's proven too small and keeping the half that might still hold the target.

\[ \text{Correct rule: if } A[\text{mid}] < \text{target}: \ \text{lo} = \text{mid} + 1 \]

Trace it on the same iteration

Why: Same comparison, correct direction: since indices 0 through 4 are proven too small, lo moves just past mid.

\[ \text{mid}=4,\ A[4]=24<40 \Rightarrow \text{lo}=\text{mid}+1=5 \]

The invariant survives this update

Why: The target's real index, 6, is still inside the new range 5 through 9. Nothing that could contain the target was discarded.

\[ \text{target index}=6 \in \text{lo}..\text{hi} = 5..9 \]

The correct search reaches the right answer

Why: Continuing this correct rule (as traced in an earlier slide) lands on index 6 and finds A[6] equal to 40 — the true, correct result.

68. Break it on purpose: an invariant stated but not actually…

Break the constraint

Discussion prompt

The rule this trap just fixed:

Same comparison, correct direction: since indices 0 through 4 are proven too small, lo moves just past mid.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

mid is 4 and A[4] is 24, which is less than 40. The buggy rule shrinks from the top instead of the bottom.

69. The three-obligation recipe for proving any loop invariant

Pattern

1. Write the invariant precisely

Why: Name exactly what stays true, in terms of the loop's own variables. A vague invariant cannot be checked, let alone proven.

2. Prove Initialization

Why: Show the invariant holds before the loop body ever executes, usually by checking the starting values of the variables directly.

3. Prove Maintenance

Why: Assume the invariant is true right before an arbitrary iteration, then show that the specific update the loop body performs keeps it true — check every branch the loop body can take, separately.

4. Prove Termination

Why: Combine the invariant with the exact condition that stopped the loop, and show together they deliver the result you actually claimed.

70. Where this shows up: Binary Search & Analyzing Loops

Real world

Discussion prompt

Outside this lesson: where does Binary Search & Analyzing Loops actually turn up? Name one concrete situation — a job, a piece of software someone ships, a decision somebody has to make — and say which part of The three-obligation recipe for proving any loop invariant is doing the work in it.

Hint: Vague is the failure mode here. "Engineering" is not a situation; "deciding whether this build is fast enough to ship" is.

Answer:

That deck traces binary search step by step on a concrete sorted array, gives the loop invariant that proves it correct with all three obligations spelled out, explains why halving the range gives logarithmic running time, and states the general rule for counting any loop's running time. It targets the off-by-one that never shrinks the range, trusting binary search on unsorted data, mixing up linear with logarithmic growth, and invariants that are stated but not actually preserved.

71. Counting the work a loop does

Picture it

Animation

Shows: Counting the work a loop does — a rendered Manim animation.

Rendered with Manim.

Takeaway: Triangular, not rectangular — but still quadratic.

72. Check yourself: which obligation is broken?

Check

Recall the buggy code from the previous trap: whenever A[mid] is less than the target, it moves hi down to just below mid, instead of moving lo up to just past mid.

Check your understanding

Which obligation does this bug violate?

  • A. Maintenance — the update can throw away the half that still contains the target. (correct)
  • B. Initialization — lo and hi are not set to the correct starting values.
  • C. Termination — the loop never stops running.
  • D. None of them — the code still returns the correct answer, just more slowly.

Answer: A

Why: The bug only affects what happens inside the loop body: when A[mid] is less than the target, discarding indices mid through hi can remove the very half that actually contains the target, since a correct search would know the target could only be at mid + 1 through hi in that case. That is a Maintenance failure — the invariant, true before the iteration, is no longer true right after it.

Why B tempts people
Initialization only concerns the values of lo and hi before the loop starts; those are still set correctly to 0 and length minus one in the buggy version.
Why C tempts people
This particular bug does shrink the range each time (hi decreases), so the loop still terminates — it just terminates having thrown away the correct answer along the way.
Why D tempts people
The trace showed the buggy code reporting 'not found' for a target of 40 that is actually present in the array at index 6 — that is a wrong answer, not merely a slow one.

73. Boundary Handling

Section

Section 3

74. Computing mid safely with floor division

Concept

Mid is computed by averaging lo and hi and rounding down. Rounding down matters: it guarantees mid always lands on a real index that's actually inside the current range.

\[ \text{mid} = \left\lfloor \frac{\text{lo}+\text{hi}}{2} \right\rfloor \]

75. Updating lo and hi so the range actually shrinks

Concept

After checking A[mid], the loop must move a boundary PAST mid, never back onto mid. Landing on mid again leaves the range exactly the same size, and the loop makes no progress at all.

\[ \text{if } A[\text{mid}] < \text{target}:\ \text{lo}=\text{mid}+1 \qquad \text{if } A[\text{mid}] > \text{target}:\ \text{hi}=\text{mid}-1 \]

76. Something is wrong here: off-by-one — lo = mid instead of mid + 1

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student writes: whenever A[mid] is less than the target, set lo = mid — forgetting the plus one.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: mid is 5, and A[5] is 31, which is less than 40.

The correct rule always moves lo strictly past mid.

Why: mid is 5, and A[5] is 31, which is less than 40. The buggy rule sets lo back to 5 — exactly where it already was.

77. Trap: off-by-one — lo = mid instead of mid + 1

Trap

The trap

A student writes: whenever A[mid] is less than the target, set lo = mid — forgetting the plus one.

\[ \text{Buggy rule: if } A[\text{mid}] < \text{target}:\ \text{lo} = \text{mid}\ (\text{missing } +1) \]

Pick up the search for 40 at lo = 5, hi = 6

Why: mid is 5, and A[5] is 31, which is less than 40. The buggy rule sets lo back to 5 — exactly where it already was.

\[ \text{mid}=\left\lfloor\frac{5+6}{2}\right\rfloor=5,\ A[5]=31<40 \Rightarrow \text{lo}=\text{mid}=5\ (\text{no change}) \]

The very next iteration repeats the exact same state

Why: lo is still 5, hi is still 6, so mid is computed as 5 again, A[5] is still 31, and the buggy rule sets lo to 5 again. Nothing about the range has changed.

\[ \text{lo}=5,\ \text{hi}=6 \to \text{mid}=5 \to \text{lo}=5,\ \text{hi}=6 \to \cdots\ (\text{forever}) \]

The fix

The correct rule always moves lo strictly past mid.

\[ \text{Correct rule: if } A[\text{mid}] < \text{target}:\ \text{lo} = \text{mid}+1 \]

Same starting point, correct update

Why: mid is 5, A[5] is 31, still less than 40 — but now lo moves to mid plus one.

\[ \text{mid}=5,\ A[5]=31<40 \Rightarrow \text{lo}=\text{mid}+1=6 \]

The range genuinely shrinks

Why: lo and hi both now sit at 6 — a range of size one — letting the loop reach its found case on the very next iteration, instead of spinning forever.

\[ \text{lo}=6,\ \text{hi}=6 \Rightarrow \text{one step from done} \]

78. Say it in words: Trap: off-by-one — lo = mid instead of mid + 1

Translation

\( \text{Buggy rule: if } A[\text{mid}] < \text{target}:\ \text{lo} = \text{mid}\ (\text{missing } +1) \)

Draw it

Translate both ways. First write the expression above as a sentence with no symbols in it at all. Then cover it, and write your sentence back as notation. If the two versions disagree, the disagreement is the thing to fix.

79. Rule out three: Check yourself: does this update shrink the range?

Elimination

Eliminate the wrong options

What happens on the very next iteration?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. lo stays 4 and hi stays 5, so mid is computed as 4 again — no progress is made.
  • B. lo becomes 5, matching what the correct update would produce.
  • C. hi becomes 3, shrinking the range from the top.
  • D. The loop immediately returns the correct answer.

Survives elimination: A

Why: Here mid equals lo already (both are 4), so setting lo = mid changes nothing at all: lo is still 4. The next iteration recomputes the exact same mid = 4 from the exact same lo = 4, hi = 5 — and if A[mid] is still less than the target, the loop repeats this identical state forever, an infinite loop rather than progress.

80. Check yourself: does this update shrink the range?

Check

Suppose lo = 4 and hi = 5, so mid works out to 4. A[mid] turns out to be less than the target, and the code (buggily) sets lo = mid instead of mid + 1.

Check your understanding

What happens on the very next iteration?

  • A. lo stays 4 and hi stays 5, so mid is computed as 4 again — no progress is made. (correct)
  • B. lo becomes 5, matching what the correct update would produce.
  • C. hi becomes 3, shrinking the range from the top.
  • D. The loop immediately returns the correct answer.

Answer: A

Why: Here mid equals lo already (both are 4), so setting lo = mid changes nothing at all: lo is still 4. The next iteration recomputes the exact same mid = 4 from the exact same lo = 4, hi = 5 — and if A[mid] is still less than the target, the loop repeats this identical state forever, an infinite loop rather than progress.

Why B tempts people
That is what the CORRECT update, lo = mid + 1, would produce. The question asks about the buggy update lo = mid, which does not add one and so does not move lo forward.
Why C tempts people
hi only changes when A[mid] is greater than the target; here A[mid] is stated to be less than the target, so hi is untouched under either the buggy or correct version.
Why D tempts people
Nothing in the scenario indicates A[mid] equals the target — it's stated to be less than the target, so the loop has no reason to return an answer yet.

81. Inclusive endpoints: why hi starts at length minus one

Concept

An array's valid indices run from 0 up to one less than its length — never up to the length itself. Setting hi to the length would point one step past the last real element.

\[ n = \text{array length} \quad \Rightarrow \quad \text{valid indices: } 0 \text{ through } n-1 \quad \Rightarrow \quad \text{hi}=n-1 \]

82. Where does each piece belong: Binary Search & Analyzing Loops

Sorting

Sort into buckets

These are the pieces of Binary Search & Analyzing Loops, out of order. Put each one back under the part of the lesson it belongs to.

Meeting Binary Search
The problem binary search solves; Why binary search beats scanning one by one; Binary search needs the array sorted
The Loop Invariant
What a loop invariant is, in plain English; An invariant is a promise that must never break; Binary search's invariant, stated precisely
Boundary Handling
Computing mid safely with floor division; Updating lo and hi so the range actually shrinks; Inclusive endpoints: why hi starts at length minus one
s1
Meeting Binary Search is where Binary Search & Analyzing Loops puts The problem binary search solves, Why binary search beats scanning one by one, Binary search needs the array sorted. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s2
The Loop Invariant is where Binary Search & Analyzing Loops puts What a loop invariant is, in plain English, An invariant is a promise that must never break, Binary search's invariant, stated precisely. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s3
Boundary Handling is where Binary Search & Analyzing Loops puts Computing mid safely with floor division, Updating lo and hi so the range actually shrinks, Inclusive endpoints: why hi starts at length minus one. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.

83. What has to be given first: Trace the one-element-array boundary case

Missing information

Discussion prompt

Search an array holding just a single value, 9, for the target 9.

What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.

Hint: Anything you would have to invent to get started is a thing the problem must supply.

Answer:

With only one element, lo and hi both land on the array's only index, 0.

84. Trace the one-element-array boundary case

Worked example

Search an array holding just a single value, 9, for the target 9.

indexvalue
09

Initialize lo and hi

Why: With only one element, lo and hi both land on the array's only index, 0.

\[ n=1,\quad \text{lo}=0,\quad \text{hi}=n-1=0 \]

Compute mid and compare

Why: With lo equal to hi, mid is forced to be 0, the only index available.

\[ \text{mid}=0,\quad A[0]=9=9 \Rightarrow \text{found at index }0 \]

Verify a different target on the very same array

Why: Searching this same one-element array for 5 instead: A[0] is 9, which is greater than 5, so hi becomes -1, and lo (0) is now greater than hi (-1) — the loop correctly reports 5 as absent, matching the fact that 5 truly isn't in this array.

\[ A[0]=9>5 \Rightarrow \text{hi}=-1 \Rightarrow \text{lo}=0 > \text{hi}=-1: \text{``not found''} \]

85. Amortised cost of a doubling array

Picture it

Animation

Shows: Amortised cost of a doubling array — a rendered Manim animation.

Rendered with Manim.

Takeaway: Expensive rarely, cheap usually — average it over the whole sequence.

86. Check yourself: trace the single-element case

Check

Consider the one-element array containing just the value 9, and a search for the target 9.

Check your understanding

How many iterations does binary search run before returning an answer?

  • A. 1 (correct)
  • B. 0
  • C. 2
  • D. Infinitely many — a one-element array never terminates

Answer: A

Why: With lo = hi = 0, the loop still runs its body once: it computes mid = 0, compares A[0] = 9 to the target 9, finds an exact match, and returns — exactly one iteration, not zero.

Why B tempts people
The loop must execute its body at least once to compute mid and perform the comparison, even when the range has just a single element; it cannot skip straight to an answer without checking.
Why C tempts people
There is no need for a second iteration here: the very first comparison, at mid = 0, already finds the target directly.
Why D tempts people
A one-element range still satisfies lo is less than or equal to hi, so the loop body runs; after that single check the search is done, not stuck looping forever.

87. Counting Loop Running Time

Section

Section 4

88. The general rule: work per iteration times number of iterations

Concept

To count how long ANY loop takes, multiply two numbers: how much work happens inside one pass through the loop body, and how many times the loop body actually runs.

\[ \text{total time} = (\text{work per iteration}) \times (\text{number of iterations}) \]

89. Cost per step times how many steps, like a recipe

Intuition

It's the same arithmetic as timing a chore: if folding one shirt takes a few seconds, and you have a pile of shirts, the total time is seconds-per-shirt times number-of-shirts.

The real work in analyzing a loop is figuring out those two numbers honestly — and the number of iterations is usually the trickier one to pin down.

90. Process: counting the iterations the obvious way

Intuition

Watch me not know the answer. This is what the first two minutes actually look like.

We want the number of passes binary search makes on an array of n elements.

Try counting passes directly: how many elements get eliminated?

Why: First pass kills about n/2 elements, second kills about n/4, and so on. Add those up and you should get the count.

That sum answers a different question

Why: Those numbers add to roughly n — which is how many elements were eliminated, not how many passes happened. You counted the wrong thing.

Dead end. Not a mistake — a move that was worth trying and did not pay off. This happens in most proofs.

Back up. Count the halvings, not the elements

Why: The window size is what shrinks: n, then n/2, then n/4. Ask how many halvings it takes to reach 1 and you are counting passes directly.

\[ \frac{n}{2^{k}} = 1 \;\Longleftrightarrow\; 2^{k} = n \;\Longleftrightarrow\; k = \log_{2} n \]

Every logarithm in this course arrives this way. Something halves, and you count the halvings.

The expert does not see the whole path in advance. The expert tries something, reads the result, and adjusts. That is the skill.

91. How many times can you halve n before reaching 1

Concept

Binary search's iteration count comes down to one question: starting from a range of size n, about how many times can you cut that size in half before only one item is left?

\[ n \to \frac{n}{2} \to \frac{n}{4} \to \cdots \to 1 \quad \text{after about } \log_2 n \text{ steps} \]

92. Plan first: Count the halvings for n = 16

Step zero

Discussion prompt

Count the halvings for n = 16 — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Halve repeatedly

Answer:

  1. Halve repeatedly
  2. Count the halvings
  3. Verify what this means for binary search's worst case

93. Count the halvings for n = 16

Worked example

Before tying this to binary search's exact iteration count, do the raw arithmetic: how many times can you cut 16 in half before reaching 1?

\[ n=16 \]

Halve repeatedly

Why: Each halving cuts the remaining size roughly in two, rounding down when needed.

\[ 16 \to 8 \to 4 \to 2 \to 1 \]

Count the halvings

Why: That took exactly 4 halvings, which matches log base two of 16 exactly, since 16 is 2 to the 4th power.

\[ \lfloor \log_2 16 \rfloor = 4 \quad (\text{4 halvings shrink 16 down to 1}) \]

Verify what this means for binary search's worst case

Why: After 4 halvings the range has shrunk to size one — but the loop still has to run one more time to actually examine that last remaining element and either match it or come up empty. So a 16-element array takes at most 5 iterations in the worst case, not 4 — confirmed by directly simulating a worst-case search on 16 elements.

\[ 4\ (\text{halvings}) + 1\ (\text{final check}) = 5\ (\text{worst-case iterations}) \]

94. Count the halvings for n = 16 — line by line

Picture it

Animation

Shows: Each line of the worked example "Count the halvings for n = 16", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: After 4 halvings the range has shrunk to size one — but the loop still has to run one more time to actually examine that last remaining element and either match it or come up empty. So a 16-element array takes at most 5 iterations in the worst case, not 4 — confirmed by directly simulating a worst-case search on 16 elements.

95. What feels wrong about this comparison?

Intuition

What feels wrong about this?

Two algorithms on a million elements. One does about a million steps. The other does about twenty.

\[ n = 1{,}000{,}000 \qquad \log_{2} n \approx 20 \]

_Plain English only. No notation, no algebra. Just say what bothers you._

The feeling: twenty feels far too small for a million items — it does not feel like it could have even looked at them.

That feeling is the proof. It is not a substitute for the proof — it is the thing the proof writes down.

It did not look at them, and that is the point. The right question is not how many items are there but how many times can this shrink before it is done, and those are wildly different numbers.

96. State the rule before it runs: Count the halvings for n = 1,000,000

Hypothesis

Predict first

Count the halvings for n = 1,000,000 is about to be worked. State your hypothesis first: which rule or definition decides this one, and what is the first move it forces? Then watch whether the example agrees with you.

Correct: Bracket n between two powers of two

Why: 2 to the 19th is 524,288, still under a million; 2 to the 20th is 1,048,576, already over a million.

A hypothesis you wrote down is falsifiable; a vague sense of how it will go is not. If the example opens somewhere else, that gap is the thing worth chasing.

97. Count the halvings for n = 1,000,000

Worked example

Now try a much bigger array — one million elements.

\[ n = 1{,}000{,}000 \]

Bracket n between two powers of two

Why: 2 to the 19th is 524,288, still under a million; 2 to the 20th is 1,048,576, already over a million.

\[ 2^{19}=524{,}288 \le 1{,}000{,}000 < 2^{20}=1{,}048{,}576 \]

Read off the raw halving count

Why: This bracketing means 19 halvings (via rounding down at each step) shrink 1,000,000 all the way down to 1.

\[ \lfloor \log_2(1{,}000{,}000) \rfloor = 19 \quad (\text{19 raw halvings}) \]

Verify the binary search worst case

Why: Just as with n = 16, add one more iteration for the loop to actually examine that final size-one range. 19 halvings plus 1 gives 20 — confirmed by directly simulating a worst-case search on one million elements, which indeed takes exactly 20 iterations.

\[ 19 + 1 = 20\ (\text{worst-case iterations for one million elements}) \]

98. Count the halvings for n = 1,000,000 — line by line

Picture it

Animation

Shows: Each line of the worked example "Count the halvings for n = 1,000,000", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: Just as with n = 16, add one more iteration for the loop to actually examine that final size-one range. 19 halvings plus 1 gives 20 — confirmed by directly simulating a worst-case search on one million elements, which indeed takes exactly 20 iterations.

99. Answer it before you see the options: Check yourself: how many iterations for…

Prediction

Predict first

Taking into account that binary search still needs to examine that final single-element range, what is the worst-case number of iterations for n = 1024?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: 11 — the 10 halvings plus one more iteration to check the last remaining element.

Why: 1024 is exactly 2 to the 10th power, so shrinking a range of size 1024 down to size 1 takes 10 halvings. But the loop still runs once more to actually examine that final size-one range and either confirm a match or come up empty — one more iteration than the halving count. So the worst case is 10 plus 1, which is 11 iterations.

100. Check yourself: how many iterations for n = 1024?

Check

A sorted array has exactly 1024 elements. Since 1024 is exactly 2 to the 10th power, repeatedly halving 1024 down to a range of size 1 takes exactly 10 halvings.

Check your understanding

Taking into account that binary search still needs to examine that final single-element range, what is the worst-case number of iterations for n = 1024?

  • A. 11 — the 10 halvings plus one more iteration to check the last remaining element. (correct)
  • B. 10 — exactly the number of halvings needed to shrink 1024 down to 1.
  • C. 1024 — one iteration per element, in the worst case.
  • D. 20 — double the halving count, since binary search checks two things each iteration.

Answer: A

Why: 1024 is exactly 2 to the 10th power, so shrinking a range of size 1024 down to size 1 takes 10 halvings. But the loop still runs once more to actually examine that final size-one range and either confirm a match or come up empty — one more iteration than the halving count. So the worst case is 10 plus 1, which is 11 iterations.

Why B tempts people
10 counts only the halvings that shrink the range down to size one; it misses the final iteration where the loop actually examines that last remaining element.
Why C tempts people
1024 would be the worst case for a loop that checks one element at a time without eliminating any others (linear search), not for a loop that discards about half the remaining range every iteration.
Why D tempts people
Binary search performs one comparison and one boundary update per iteration, not two separate iterations' worth of work — there's no reason to double the halving count.

101. Binary search's iteration count is logarithmic

Concept

Tie the halving-count idea directly to binary search: because each iteration discards about half of whatever range remains, the number of iterations needed is about log base two of n.

\[ \text{number of iterations} \le \lfloor \log_2 n \rfloor + 1 \]

That extra plus-one accounts for the loop's very last check, once the range has shrunk down to a single element. It doesn't change the overall growth rate — logarithmic is logarithmic, with or without one extra step.

102. Where binary search goes wrong

Picture it

Animation

Shows: Where binary search goes wrong — a rendered Manim animation.

Rendered with Manim.

Takeaway: A range that fails to shrink is an infinite loop, not a bug in the maths.

103. Work per iteration is constant time

Concept

Inside one iteration, binary search does a small, fixed amount of work: compute mid, look up one array value, do one comparison, and update a boundary. None of that grows as the array gets bigger.

constant time — Work that takes the same small amount of time regardless of how large the input is. One array lookup by index is constant time, because the array supports jumping straight to any position.

104. Multiplying a constant by log n is still log n

Concept

Apply the general rule: work per iteration is constant, and the number of iterations is about log base two of n.

\[ \text{total time} = O(1) \times O(\log_2 n) = O(\log_2 n) \]

That earlier plus-one detail, buried inside the exact iteration count, is exactly the kind of small constant Big-O notation is built to absorb — which is why we simply write O(log n).

105. Complete the line: The move behind every logarithm in this course

Fill the middle

Fill in the blanks

From The move behind every logarithm in this course — finish the line. Write what belongs on the right of the equals sign before you look.

c \cdot \log_\Theta(\log n) n = ___

Why: Producing the right-hand side unprompted is the difference between recognising this line and being able to use it. Whenever a quantity is divided by a constant factor each pass, the pass count is a logarithm.

106. The move behind every logarithm in this course

Concept

The move: #10 (Halve and count), then #4 (Replace with the dominant term).

Halve and count

Why: Whenever a quantity is divided by a constant factor each pass, the pass count is a logarithm. Divided by 2 gives log base 2, by 3 gives log base 3 — and the base washes out inside big-O.

Then replace with the dominant term

Why: Constant work per pass times log n passes is c times log n, and move #4 says the constant is not part of the growth rate.

\[ c \cdot \log_{2} n = \Theta(\log n) \]

Contrast this with subtracting one each pass, which gives n passes. Divide gives a log; subtract gives a linear count. That single distinction explains most of the running times in the next four lessons.

107. Decode the notation: The move behind every logarithm in this course

Notation

Annotate

From The move behind every logarithm in this course — read this one piece at a time. What is each part doing?

On: \( c \cdot \log_{2} n = \Theta(\log n) \)

  • Whenever a quantity is divided by a constant factor each pass, the pass count is a logarithm. Divided by 2 gives log base 2, by 3 gives log base 3 — and the base washes out inside big-O.
  • Constant work per pass times log n passes is c times log n, and move #4 says the constant is not part of the growth rate.

108. Guess the shape of the answer: Contrast: linear search's iteration count is…

Estimation

Predict first

Linear search checks index 0, then 1, then 2, and so on, comparing each value to the target until it finds a match or runs out of array.

Commit before you compute: what does Contrast: linear search's iteration count is linear come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.

Correct: Verify with the 10-element array

Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. Searching our familiar 10-value array for a missing target with linear search takes up to 10 comparisons — dramatically more than binary search's 4 iterations on the very same array.

109. Contrast: linear search's iteration count is linear

Worked example

Linear search checks index 0, then 1, then 2, and so on, comparing each value to the target until it finds a match or runs out of array.

Work per iteration is constant

Why: Each step is just one comparison — the same small amount of work as a binary search iteration.

\[ \text{work per iteration} = O(1) \]

Number of iterations in the worst case is n

Why: If the target is missing (or happens to be the very last element), linear search must check every one of the n positions before it can stop.

\[ \text{worst-case iterations} = n \]

Verify with the 10-element array

Why: Searching our familiar 10-value array for a missing target with linear search takes up to 10 comparisons — dramatically more than binary search's 4 iterations on the very same array. Multiplying constant work by n iterations gives O(n), linear time, not O(log n).

110. Contrast: linear search's iteration count is linear — line by line

Picture it

Animation

Shows: Each line of the worked example "Contrast: linear search's iteration count is linear", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: Searching our familiar 10-value array for a missing target with linear search takes up to 10 comparisons — dramatically more than binary search's 4 iterations on the very same array. Multiplying constant work by n iterations gives O(n), linear time, not O(log n).

111. Something is wrong here: mistaking a halving counter for a linear one

Anomaly

Predict first

A student writes this, and it looks reasonable:

A loop starts with i equal to 1 and doubles i every iteration (i becomes i times 2), stopping once i is at least n = 16. A student reasons: it's one loop with one line inside, and i counts up to 16, so it must take about 16 iterations — linear in n.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: The reasoning treats every loop that 'counts up to n' as if it always adds a fixed amount each time.

Trace the loop for real, tracking exactly how i changes on each pass.

Why: The reasoning treats every loop that 'counts up to n' as if it always adds a fixed amount each time. But i here doubles, it doesn't add 1 — and that difference changes everything about how fast it reaches 16.

112. Trap: mistaking a halving counter for a linear one

Trap

The trap

A loop starts with i equal to 1 and doubles i every iteration (i becomes i times 2), stopping once i is at least n = 16. A student reasons: it's one loop with one line inside, and i counts up to 16, so it must take about 16 iterations — linear in n.

\[ \text{Wrong claim: iterations} \approx n = 16 \]

This ignores HOW i changes each time

Why: The reasoning treats every loop that 'counts up to n' as if it always adds a fixed amount each time. But i here doubles, it doesn't add 1 — and that difference changes everything about how fast it reaches 16.

The fix

Trace the loop for real, tracking exactly how i changes on each pass.

\[ i:\ 1 \to 2 \to 4 \to 8 \to 16\ (\text{stop, since } 16 \text{ is no longer} < 16) \]

Count the actual iterations

Why: The loop body ran exactly 4 times (while i was 1, 2, 4, and 8) before the condition failed at i = 16.

\[ \text{iterations} = 4 = \log_2 16 \]

Generalize correctly

Why: Since i doubles every iteration, the number of iterations needed to reach or exceed n is about log base two of n, not n itself. A doubling (or halving) counter always signals logarithmic running time.

\[ i \text{ doubles each step} \Rightarrow \text{iterations} \approx \log_2 n \quad (\text{not } O(n)) \]

113. Which of these survive contact with Binary Search & Analyzing Loops?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
You have named 8 reusable moves so far. Say as many as you can out loud, by number, from memory.; You have a sorted list of values and a target value. You want to find the target's position — or learn that it isn't there — using as few comparisons as possible.; Checking every position left to right always works, but in the worst case it can take as many comparisons as there are items in the list.
Breaks
A student runs binary search directly on this array, which is NOT sorted, searching for the target 3 (which is actually present, at index 3).; Trace it on the search for 40, iteration 1
sound
These are stated as this lesson states them — each one survives the edge cases Binary Search & Analyzing Loops puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

114. Answer it before you see the options: Check yourself: classify this loop's…

Prediction

Predict first

Using the rule 'work per iteration times number of iterations,' how should this loop's running time be classified in terms of n?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: Logarithmic — O(log n), since count shrinks by half each iteration rather than by a fixed amount.

Why: What matters is how count changes: it is halved each iteration, not decreased by a fixed amount. Halving repeatedly reaches 0 in about log base two of n iterations. Work per iteration is constant (one division and one comparison), so total time is O(1) times O(log n), which is O(log n).

115. Check yourself: classify this loop's running time

Check

A loop starts with count equal to n, and divides count by 2 (rounding down) every iteration, stopping when count reaches 0.

Check your understanding

Using the rule 'work per iteration times number of iterations,' how should this loop's running time be classified in terms of n?

  • A. Logarithmic — O(log n), since count shrinks by half each iteration rather than by a fixed amount. (correct)
  • B. Linear — O(n), since count runs all the way from n down to 0.
  • C. Constant — O(1), since there is only one line of work inside the loop.
  • D. Quadratic — O(n squared), since dividing is more complex than adding.

Answer: A

Why: What matters is how count changes: it is halved each iteration, not decreased by a fixed amount. Halving repeatedly reaches 0 in about log base two of n iterations. Work per iteration is constant (one division and one comparison), so total time is O(1) times O(log n), which is O(log n).

Why B tempts people
O(n) describes a loop whose counter changes by a FIXED amount each iteration, like subtracting 1. Halving reaches 0 far faster than n steps would.
Why C tempts people
Constant time means the loop runs a fixed number of times no matter how large n is. Here the number of iterations grows (slowly) as n grows, so it is not constant.
Why D tempts people
One line of constant work per iteration times about log n iterations gives O(log n), not O(n squared) — there's no nested loop or repeated work hiding here.

116. Bringing It Together

Section

Section 5

117. Why halving beats subtracting one, side by side

Concept

Compare two loops that both shrink a range from n down to nothing: one subtracts 1 each iteration, the other halves each iteration. The gap between their iteration counts grows enormous as n gets large.

nsubtract-1 iterationshalving iterations (worst case)
10104
1,000,0001,000,00020

118. Fill in: subtract-1 iterations for Why halving beats subtracting one, side by…

Comparison

Comparison matrix

From Why halving beats subtracting one, side by side: refill the subtract-1 iterations column from what you know. The rest of the table is as it appeared.

nsubtract-1 iterationshalving iterations (worst case)
10104
1,000,0001,000,00020

119. A halving loop is logarithmic

Picture it

Animation

Shows: A halving loop is logarithmic — a rendered Manim animation.

Rendered with Manim.

Takeaway: How the counter changes decides the shape, not how it is written.

120. What has to happen first: Full end-to-end trace with running time annotated

Ranking

Put in order

Put the moves of Full end-to-end trace with running time annotated into the order they have to happen.

  1. Recall the full trace
  2. Compare the iteration count to the predicted bound
  3. Verify by counting the rows in the trace table

Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. This is the same trace worked through earlier, laid out iteration by iteration.

121. Full end-to-end trace with running time annotated

Worked example

Return to the search for 40 in the familiar 10-value array, this time counting iterations against the predicted bound.

indexvalue
03
17
212
318
424
531
640
752
868
977

Recall the full trace

Why: This is the same trace worked through earlier, laid out iteration by iteration.

iterationlohimidA[mid]action
10942424 < 40, so lo = 5
25975252 > 40, so hi = 6
35653131 < 40, so lo = 6
46664040 = 40, found at index 6

Compare the iteration count to the predicted bound

Why: For n equal to 10, floor of log base two of 10 is 3, plus 1 gives 4 — exactly matching the 4 iterations actually used.

\[ \lfloor \log_2 10 \rfloor + 1 = 3 + 1 = 4 \]

Verify by counting the rows in the trace table

Why: The table above lists exactly 4 iterations, matching the predicted worst-case bound precisely — the theory and the concrete run agree.

122. Watch it run: Full end-to-end trace with running time annotated

Pattern

Step through it

Step through Full end-to-end trace with running time annotated one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: index is 0
  2. Step 2: index is 1
  3. Step 3: index is 2
  4. Step 4: index is 3
  5. Step 5: index is 4
  6. Step 6: index is 5
  7. Step 7: index is 6
  8. Step 8: index is 7
  9. Step 9: index is 8
  10. Step 10: index is 9

123. Recognizing when a problem allows binary search

Concept

Binary search doesn't strictly require an array of numbers. It only requires two things: a sorted or monotonic structure, and a way to check any single candidate in constant time.

If you can ask 'is this candidate too small, too big, or exactly right?' and the answer only ever flips from 'too small' to 'too big' as you move along, you can binary search over it.

124. Teach it back: Recognizing when a problem allows binary search

Explain it

Discussion prompt

Explain Recognizing when a problem allows binary search to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Binary search doesn't strictly require an array of numbers. It only requires two things: a sorted or monotonic structure, and a way to check any single candidate in constant time.

125. Binary search beyond arrays

Intuition

Think of guessing a person's exact age, where you're only ever told 'higher' or 'lower' after each guess. As long as the true answer flips your yes-or-no verdict exactly once as you scan from low to high, the same halving strategy applies.

This is why binary search shows up far beyond sorted arrays — searching over a whole range of possible answers, not just over a list of stored values.

126. Why the cost is logarithmic

Picture it

Animation

Shows: Why the cost is logarithmic — a rendered Manim animation.

Rendered with Manim.

Takeaway: A million elements costs twenty probes.

127. Plan first: Apply the pattern to a new array

Step zero

Discussion prompt

Apply the pattern to a new array — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Initialize lo and hi

Answer:

  1. Initialize lo and hi
  2. Iteration 1
  3. Iteration 2
  4. Iteration 3
  5. Verify against the array directly

128. Apply the pattern to a new array

Worked example

Search this different, 6-element sorted array for the target 12.

indexvalue
02
15
29
312
417
523

Initialize lo and hi

Why: 6 elements means indices 0 through 5.

\[ \text{lo}=0,\ \text{hi}=5 \]

Iteration 1

Why: mid is 2, and B[2] is 9, less than 12, so lo moves past mid.

\[ \text{mid}=2,\ B[2]=9<12 \Rightarrow \text{lo}=3 \]

Iteration 2

Why: mid is 4, and B[4] is 17, greater than 12, so hi moves before mid.

\[ \text{mid}=4,\ B[4]=17>12 \Rightarrow \text{hi}=3 \]

Iteration 3

Why: lo and hi both land on 3, mid is 3, and B[3] equals 12 exactly.

\[ \text{mid}=3,\ B[3]=12=12 \Rightarrow \text{found at index }3 \]

Verify against the array directly

Why: B[3] is genuinely 12 by direct lookup in the table above, and the search used 3 iterations — matching floor of log base two of 6, which is 2, plus 1, equal to 3.

129. Apply the pattern to a new array — line by line

Picture it

Animation

Shows: Each line of the worked example "Apply the pattern to a new array", appearing one at a time.

The same working the example does, in the order a tutor would write it.

Takeaway: B[3] is genuinely 12 by direct lookup in the table above, and the search used 3 iterations — matching floor of log base two of 6, which is 2, plus 1, equal to 3.

130. Putting the invariant and the running time together

Concept

Every binary search you write should be defended by two separate arguments: the loop invariant proves the answer is correct, and the halving argument proves it is fast.

Skip the invariant, and you cannot be sure the algorithm is right. Skip the halving argument, and you cannot be sure it's actually earning its logarithmic reputation.

131. By analogy: Putting the invariant and the running time together

Analogy

Discussion prompt

Explain Putting the invariant and the running time together by analogy to something with no CS3000 Algorithms in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Every binary search you write should be defended by two separate arguments: the loop invariant proves the answer is correct, and the halving argument proves it is fast.

132. Rule out three: Check yourself: final integrative check

Elimination

Eliminate the wrong options

What happens to this version's running time?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. It becomes linear, O(n), because now only one index is trimmed per iteration instead of about half.
  • B. It stays logarithmic, O(log n), because it still updates lo or hi based on a comparison each iteration.
  • C. It becomes constant, O(1), because mid no longer needs a division.
  • D. It becomes quadratic, O(n squared), because computing lo plus one is more expensive than a true midpoint.

Survives elimination: A

Why: Running time is work per iteration times number of iterations. The per-iteration work is still constant either way, but now each iteration removes only ONE index from the range instead of about half of it. So the number of iterations needed to empty a range of size n becomes about n, not log base two of n — that is linear, not logarithmic.

133. Check yourself: final integrative check

Check

A classmate writes their own binary search, but instead of computing mid as the true midpoint, they always set mid equal to lo plus one — moving through the range one index at a time. The rest of the comparisons and updates are otherwise correct.

Check your understanding

What happens to this version's running time?

  • A. It becomes linear, O(n), because now only one index is trimmed per iteration instead of about half. (correct)
  • B. It stays logarithmic, O(log n), because it still updates lo or hi based on a comparison each iteration.
  • C. It becomes constant, O(1), because mid no longer needs a division.
  • D. It becomes quadratic, O(n squared), because computing lo plus one is more expensive than a true midpoint.

Answer: A

Why: Running time is work per iteration times number of iterations. The per-iteration work is still constant either way, but now each iteration removes only ONE index from the range instead of about half of it. So the number of iterations needed to empty a range of size n becomes about n, not log base two of n — that is linear, not logarithmic.

Why B tempts people
Whether the running time is logarithmic depends on how much of the range is eliminated each iteration, not merely on whether a comparison happens at all. Discarding one index at a time is the same shrink rate as linear search.
Why C tempts people
Removing the division doesn't matter here — both division and addition are constant-time operations. The change that actually matters is that each iteration now eliminates only one index instead of about half the range.
Why D tempts people
There is no nested loop or repeated inner work introduced by this change; computing lo plus one is a single constant-time operation, just like computing the true midpoint was.

134. Toolkit update

Concept

Moves added today:

Moves you reused today:

Maintenance is induction wearing work clothes: it held before the pass, so it holds after is peel one off plus substitute the hypothesis, with the pass number playing the role of n.

Full toolkit so far: #1 through #10.

Next session opens with you naming every one of these from memory, before any new material.

135. Break it if you can: Toolkit update

Counterexample

Discussion prompt

Maintenance is induction wearing work clothes: it held before the pass, so it holds after is peel one off plus substitute the hypothesis, with the pass number playing the role of n.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

Next session opens with you naming every one of these from memory, before any new material.

136. Connect it up: Binary Search & Analyzing Loops

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — Meeting Binary Search · The Loop Invariant · Boundary Handling · Counting Loop Running Time · Bringing It Together. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

137. What you can do now

Recap

You now have the full toolkit: trace binary search precisely, state and prove its invariant, and count any loop's running time.

PieceWhat to check
Initializationlo and hi start at the array's edges
Maintenancethe correct half is discarded, using the sorted order
Terminationfound returns mid; an empty range means truly absent
Running timeconstant work per iteration times about log base two n iterations

Sources

  1. Binary search algorithm — Wikipedia
  2. Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms, 3rd ed., Ch. 2 problems and Ch. 3 (asymptotic notation), on binary search's O(log n) analysis — MIT Press, 2009.
  3. Every trace in this deck (lo/hi/mid arithmetic, found and not-found cases, the unsorted-data failure, the off-by-one infinite loop, and the worst-case iteration counts for n = 10, 16, 1024, and 1,000,000) was re-derived by hand and independently confirmed by executing an equivalent binary-search routine in Python, checking every (lo, hi, mid, A[mid]) row against the script's output. — Verified 2026-07-18.
  4. Northeastern University CS 3000, Algorithms and Data (Summer 2026) — course page and syllabus — course.ccs.neu.edu/cs3000su26. Sets Cormen, Leiserson, Rivest and Stein, Introduction to Algorithms (3rd ed.) as the textbook; listings follow its conventions.
  5. CS 3000 course notes and midterm references circulated by students — github.com/vigneshsaravanakumar404/CS-3000-Algorithms-Data. Notes are typeset with the algpseudocode package, which is the style the listings in this deck follow.

Want this taught 1-on-1? Alexander tutors CS3000 Algorithms — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108