Maximum Sum Subarray

This deck works the maximum sum subarray problem three ways: by brute force, by divide-and-conquer including the crossing subarray case, and by Kadane's linear scan. It targets confusing a subarray with a subsequence, forgetting the crossing case in divide-and-conquer, resetting Kadane's running sum to zero on an all-negative array, and off-by-one errors when reporting the start and end indices. Every trace is re-derived by hand on the same running example, so all three methods check against each other.

Subject: CS3000 Algorithms · 131 slides · symbolic lesson

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

What this lesson covers

The lesson, slide by slide

1. Maximum Sum Subarray

Title

CS3000 Algorithms

Three ways to find the best contiguous slice — and why the fastest one is also the simplest.

2. What you will be able to do

Objectives

By the end you can:

  1. State the maximum sum subarray problem precisely, and tell a subarray from a subsequence.
  2. Trace the brute-force approach and explain why keeping a running total beats resumming from scratch.
  1. Derive the divide-and-conquer recurrence, including why the crossing subarray case cannot be skipped.
  2. Trace Kadane's algorithm (extend or restart) and explain why it still works on an all-negative array.
  1. Report the start and end positions of the winning subarray without an off-by-one mistake.

3. What survived from Quicksort, Selection & Median of Medians?

Warm-up

Discussion prompt

Before we open Maximum Sum Subarray: without looking back, what was the main idea of Quicksort, Selection & Median of Medians, 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:

That deck covers quicksort's partition step and its recursion, the best, average, and worst-case running times, and the sorted input that triggers the worst case. It then explains randomized pivots and why they make the bad case vanishingly unlikely, introduces the selection problem and quickselect, and gives the median-of-medians pivot rule, using groups of five, that guarantees linear worst-case selection. It targets the myths that quicksort is always n log n, that selection requires a full sort, and that the choice of pivot does not matter for the worst case, and it explains why groups of five in particular are used.

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

Concept

Before any new material: cover the screen.

You have named 11 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 one move 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 11 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. The Problem

Section

Section 1

7. What counts as a subarray

Concept

A subarray is a run of elements taken from an array that are all next to each other, with nothing skipped in between. You pick a starting position and an ending position, and the subarray is everything from one to the other.

subarray — A contiguous slice of an array: a starting position and an ending position, plus every element in between. No gaps allowed.

8. Three algorithms, three curves

Picture it

Animation

Shows: Three algorithms, three curves — a rendered Manim animation.

Rendered with Manim.

Takeaway: Same answer from all three. Only the cost differs.

9. Picture an unbroken slice

Intuition

Line the array's elements up in a row, like beads on a string. A subarray is what you get by cutting the string at two points and keeping the piece in between — you cannot pull individual beads out from the middle and skip over the rest.

That single restriction — no skipping — is what makes this problem different from picking any collection of elements you like.

10. The maximum sum subarray problem

Concept

Given an array of numbers (some positive, some negative), find the contiguous subarray whose elements add up to the largest possible sum.

The subarray must be non-empty — you must pick at least one element. An empty selection is not a valid answer, even though its sum would trivially be zero.

\[ \text{Find } i \le j \text{ maximizing } \sum_{k=i}^{j} A[k] \]

11. By analogy: The maximum sum subarray problem

Analogy

Discussion prompt

Explain The maximum sum subarray problem 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:

Given an array of numbers (some positive, some negative), find the contiguous subarray whose elements add up to the largest possible sum.

12. Meet the running example

Concept

This lesson uses one array throughout, so you can check every method against the same answer.

\[ A = [\,-2,\ 1,\ -3,\ 4,\ -1,\ 2,\ 1,\ -5,\ 4\,] \]

index012345678
value A[index]-21-34-121-54

13. Picture it first: Sneak preview: eyeballing the best slice

Picture it

Figure (svg): Nine boxes in a row showing the array negative two, one, negative three, four, negative one, two, one, negative five, four, with the four boxes for four, negative one, two, one highlighted as the best-looking run.

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:

Before building any algorithm, just look at the array. Somewhere in there is one run of numbers that beats every other run. Try to spot it.

14. Sneak preview: eyeballing the best slice

Intuition

Before building any algorithm, just look at the array. Somewhere in there is one run of numbers that beats every other run. Try to spot it.

Figure (svg): Nine boxes in a row showing the array negative two, one, negative three, four, negative one, two, one, negative five, four, with the four boxes for four, negative one, two, one highlighted as the best-looking run.

That highlighted run does look promising. We will prove, three separate ways, exactly why it wins — and get the same answer every time.

15. Teach it back: Sneak preview: eyeballing the best slice

Explain it

Discussion prompt

Explain Sneak preview: eyeballing the best slice 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:

Before building any algorithm, just look at the array. Somewhere in there is one run of numbers that beats every other run. Try to spot it.

16. Something is wrong here: a subarray is not a subsequence

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student wants a big sum, so they pick whichever elements look large, skipping the ones that hurt, regardless of position.

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

Correct: This selection skips indices 1, 2, 4, 6, and 7 entirely.

A valid subarray keeps every position between the start and the end — no skipping, even if some in-between elements are negative.

Why: This selection skips indices 1, 2, 4, 6, and 7 entirely. Skipping around like this makes it a subsequence, not a subarray — the problem does not allow it.

17. Trap: a subarray is not a subsequence

Trap

The trap

A student wants a big sum, so they pick whichever elements look large, skipping the ones that hurt, regardless of position.

\[ \text{picked indices } 0, 3, 5, 8:\ -2,\ 4,\ 2,\ 4 \]

Add up the cherry-picked elements

Why: This selection skips indices 1, 2, 4, 6, and 7 entirely. Skipping around like this makes it a subsequence, not a subarray — the problem does not allow it.

\[ -2 + 4 + 2 + 4 = 8 \quad (\text{not a valid answer}) \]

The fix

A valid subarray keeps every position between the start and the end — no skipping, even if some in-between elements are negative.

\[ \text{indices } 3 \text{ through } 6:\ 4,\ -1,\ 2,\ 1 \]

Add up the contiguous run instead

Why: Every index from 3 to 6 is included, with nothing skipped. That is what makes it a legal subarray, and its sum of 6 is exactly the answer we will confirm three different ways.

\[ 4 + (-1) + 2 + 1 = 6 \]

18. Decode the notation: Trap: a subarray is not a subsequence

Notation

Annotate

From Trap: a subarray is not a subsequence — read this one piece at a time. What is each part doing?

On: \( \text{picked indices } 0, 3, 5, 8:\ -2,\ 4,\ 2,\ 4 \)

  • This selection skips indices 1, 2, 4, 6, and 7 entirely. Skipping around like this makes it a subsequence, not a subarray — the problem does not allow it.
  • Every index from 3 to 6 is included, with nothing skipped. That is what makes it a legal subarray, and its sum of 6 is exactly the answer we will confirm three different ways.

19. Why eyeballing is not enough

Intuition

Nine numbers are easy to scan by eye. A real array might have a million numbers, and the best run could start and end anywhere.

We need a systematic procedure that is guaranteed to find the best run, no matter how big the array gets, without relying on a human noticing the right pattern.

20. The plan for this lesson

Concept

We will solve the same problem three times, each version faster than the last, and check that all three agree on our running example.

  1. Brute force — try every possible subarray, guaranteed correct, but slow.
  2. Divide and conquer — split the array in half and combine the halves cleverly.
  3. Kadane's algorithm — one pass, left to right, no recursion at all.

21. Brute Force

Section

Section 2

22. Try every subarray

Concept

The most direct plan: generate every possible subarray, add up each one, and remember the biggest sum seen so far.

A subarray is fully determined by two choices: where it starts and where it ends. So we need a loop over every possible start, and for each start, a loop over every possible end.

23. The all-negative edge case

Picture it

Animation

Shows: The all-negative edge case — a rendered Manim animation.

Rendered with Manim.

Takeaway: Decide up front whether the empty subarray counts. The two answers differ.

24. Two sliding markers

Intuition

Picture two markers on the number line of positions: a start marker and an end marker, with the start marker never to the right of the end marker.

The outer loop drags the start marker across every position. For each place the start marker stops, the inner loop drags the end marker across every position from the start onward.

25. Naive resumming costs a third loop

Concept

The most naive version adds up every element between the start and end markers from scratch, every single time the end marker moves.

That inner addition is itself a loop. Start loop, end loop, and a summing loop nested inside — three loops stacked on top of each other.

\[ \text{start loop} \times \text{end loop} \times \text{sum loop} \ \Rightarrow\ \Theta(n^3) \]

26. The running-sum trick

Concept

Notice that when the end marker moves one step to the right, the new sum is just the old sum plus the one new element — there is no need to re-add everything from the start.

running sum — A single accumulating total that is updated by adding one new element at a time, instead of being recomputed from scratch.

\[ \text{sum}(i, j+1) = \text{sum}(i, j) + A[j+1] \]

27. What has to happen first: Counting subarrays: the pattern

Ranking

Put in order

Put the moves of Counting subarrays: the pattern into the order they have to happen.

  1. List the count for small sizes
  2. Spot the closed form
  3. Verify against size 5

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. For size 1 there is only 1 subarray (the single element itself).

28. Counting subarrays: the pattern

Worked example

Before tracing sums, notice how many subarrays even exist. For an array of a given size, count every choice of start and end.

List the count for small sizes

Why: For size 1 there is only 1 subarray (the single element itself). For size 2 there are 3: two single elements plus the whole pair.

array sizenumber of subarrays
11
23
36
410
515

Spot the closed form

Why: Each row matches size times (size plus one), divided by two — the same formula for summing 1 through size.

\[ \text{count}(n) = \frac{n(n+1)}{2} \]

Verify against size 5

Why: Plugging n = 5 into the formula gives 5 times 6 divided by 2, which is 15 — exactly the row above. So the brute-force loop structure really does examine that many candidates.

\[ \frac{5 \cdot 6}{2} = 15\ \checkmark \]

29. Brute force, fully traced

Worked example

Trace the running-sum brute force on a short array so every step is visible.

\[ B = [\,4,\ -1,\ 2,\ 1,\ -5\,] \]

Fix the start marker at position 0 and slide the end marker rightward

Why: Each new sum adds just one more element to the previous running sum: 4, then 4 plus negative one is 3, then 3 plus 2 is 5, then 5 plus 1 is 6, then 6 plus negative five is 1.

startbest end reachedbest sum for this start
036

Repeat for every remaining start position

Why: Slide the start marker to 1, 2, 3, and 4 in turn, and for each one slide the end marker across the rest of the array the same way.

startbest end reachedbest sum for this start
132
233
331
44-5

Verify the overall winner across all five starts

Why: Comparing the best-sum column (6, 2, 3, 1, -5), the largest is 6, achieved starting at position 0 and ending at position 3 — the subarray 4, -1, 2, 1. That matches directly re-adding those four numbers.

\[ 4 + (-1) + 2 + 1 = 6\ \checkmark \]

30. The obvious approach, and its cost

Picture it

Animation

Shows: The obvious approach, and its cost — a rendered Manim animation.

Rendered with Manim.

Takeaway: Correct, and unusable past a few thousand elements.

31. Guess the shape of the answer: Brute force on an all-negative array

Estimation

Predict first

Every algorithm in this lesson must handle the case where nothing in the array is positive. Try brute force first, since it needs no cleverness at all.

Commit before you compute: what does Brute force on an all-negative 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 overall winner is the least negative element

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. Comparing -8, -3, -6, -2, -5, -4, the largest is -2.

32. Brute force on an all-negative array

Worked example

Every algorithm in this lesson must handle the case where nothing in the array is positive. Try brute force first, since it needs no cleverness at all.

\[ C = [\,-8,\ -3,\ -6,\ -2,\ -5,\ -4\,] \]

Extend each start marker and watch every running sum only get worse

Why: Adding another negative number never increases a running sum, so for every start position the best subarray is just that one single element by itself.

startbest single-element sum
0-8
1-3
2-6
3-2
4-5
5-4

Verify the overall winner is the least negative element

Why: Comparing -8, -3, -6, -2, -5, -4, the largest is -2. Since the subarray must be non-empty, -2 is the correct answer here — not zero.

\[ \max(-8,-3,-6,-2,-5,-4) = -2\ \checkmark \]

33. Brute force on an all-negative array — line by line

Picture it

Animation

Shows: Each line of the worked example "Brute force on an all-negative array", appearing one at a time.

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

Takeaway: Comparing -8, -3, -6, -2, -5, -4, the largest is -2. Since the subarray must be non-empty, -2 is the correct answer here — not zero.

34. Something is wrong here: off-by-one when reporting the answer

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student's loop uses the end marker as a plain counter and reports it directly as the ending position, without checking what it actually points to.

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

Correct: If the loop's end marker was really pointing one position past the last element actually included, reporting it directly includes one extra element that was never summed — the reported slice does not match the reported…

Always double-check what the last-updated marker actually points to: the last position included in the sum, not one past it.

Why: If the loop's end marker was really pointing one position past the last element actually included, reporting it directly includes one extra element that was never summed — the reported slice does not match the reported sum.

35. Trap: off-by-one when reporting the answer

Trap

The trap

A student's loop uses the end marker as a plain counter and reports it directly as the ending position, without checking what it actually points to.

\[ \text{best sum found while end marker } = 4 \text{ (one past the last included element)} \]

Report start 0, end 4 as the winning subarray

Why: If the loop's end marker was really pointing one position past the last element actually included, reporting it directly includes one extra element that was never summed — the reported slice does not match the reported sum.

\[ \text{claims } A[0..4],\ \text{but the sum 6 only used } A[0..3] \]

The fix

Always double-check what the last-updated marker actually points to: the last position included in the sum, not one past it.

\[ \text{best sum 6 came from summing exactly } A[0], A[1], A[2], A[3] \]

Report start 0, end 3, and re-add to confirm

Why: Re-summing the reported range must reproduce the reported total exactly. Here 4 plus negative one plus 2 plus 1 is 6, matching the claimed sum, so the indices are trustworthy.

\[ A[0]+A[1]+A[2]+A[3] = 4-1+2+1 = 6\ \checkmark \]

36. Brute force time complexity

Concept

With the running-sum trick, the summing loop disappears, leaving just the start loop and the end loop.

\[ \text{start loop} \times \text{end loop} \ \Rightarrow\ \Theta(n^2) \]

This is correct and simple, and it is the baseline every faster method must agree with. But quadratic growth becomes a real problem as the array grows.

37. How slow is quadratic, really?

Intuition

Imagine an array with one hundred thousand numbers in it — not unusual for real data.

\[ n = 100{,}000 \ \Rightarrow\ n^2 = 10{,}000{,}000{,}000 \text{ operations} \]

Ten billion operations is the kind of number that turns an instant answer into a coffee break. This is exactly the motivation for the faster methods that follow.

38. Divide and Conquer

Section

Section 3

39. The divide-and-conquer idea

Concept

Split the array into a left half and a right half at a middle position, solve each half the same way, and then combine the two answers.

Name the middle position clearly: call it the midpoint. The left half runs from the start of the array up to and including the midpoint; the right half runs from just after the midpoint to the end.

\[ \text{midpoint} = \left\lfloor \frac{\text{first index} + \text{last index}}{2} \right\rfloor \]

40. Picture it first: Three hiding places for the best subarray

Picture it

Figure (svg): The nine-element array split into a left half of five boxes and a right half of four boxes, with a dashed line at the midpoint boundary.

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:

Once the array is cut in half, the true best subarray has exactly three possible hiding places.

41. Three hiding places for the best subarray

Intuition

Once the array is cut in half, the true best subarray has exactly three possible hiding places.

Figure (svg): The nine-element array split into a left half of five boxes and a right half of four boxes, with a dashed line at the midpoint boundary.

  1. Entirely inside the left half.
  2. Entirely inside the right half.
  3. Crossing the boundary — straddling both halves at once.

42. The prefix-sum view

Picture it

Animation

Shows: The prefix-sum view — a rendered Manim animation.

Rendered with Manim.

Takeaway: A second route to linear time, and the one that generalises.

43. Why exactly these three cases

Concept

Take any subarray at all. Either every one of its positions is at or before the midpoint (case one), or every position is after the midpoint (case two), or it includes at least one position from each side (case three).

There is no fourth possibility, and no subarray can belong to more than one case at once. The three cases are exhaustive and mutually exclusive, so checking all three is both necessary and sufficient.

44. Where does each piece belong: Maximum Sum Subarray

Sorting

Sort into buckets

These are the pieces of Maximum Sum Subarray, out of order. Put each one back under the part of the lesson it belongs to.

The Problem
What counts as a subarray; Picture an unbroken slice; The maximum sum subarray problem
Brute Force
Try every subarray; Two sliding markers; Naive resumming costs a third loop
Divide and Conquer
The divide-and-conquer idea; Three hiding places for the best subarray; Why exactly these three cases
s1
The Problem is where Maximum Sum Subarray puts What counts as a subarray, Picture an unbroken slice, The maximum sum subarray problem. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s2
Brute Force is where Maximum Sum Subarray puts Try every subarray, Two sliding markers, Naive resumming costs a third loop. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.
s3
Divide and Conquer is where Maximum Sum Subarray puts The divide-and-conquer idea, Three hiding places for the best subarray, Why exactly these three cases. Knowing which part of the lesson a problem belongs to is most of knowing which method to reach for.

45. The crossing subarray, defined precisely

Concept

A crossing subarray is one that includes the midpoint position itself and the position right after it — it reaches across the cut.

crossing subarray — A subarray whose range includes both the last index of the left half and the first index of the right half, so it cannot be found by looking at either half alone.

46. Term to definition: Maximum Sum Subarray

Matching

Match the pairs

Match each term to the definition this lesson gave it — not the one you would guess from the word.

  • t1. subarray
  • t2. running sum
  • t3. crossing subarray
  • d1. A contiguous slice of an array: a starting position and an ending position, plus every element in between. No gaps allowed.
  • d2. A single accumulating total that is updated by adding one new element at a time, instead of being recomputed from scratch.
  • d3. A subarray whose range includes both the last index of the left half and the first index of the right half, so it cannot be found by looking at either half alone.

Why: These are the working definitions of subarray, running sum, crossing subarray as Maximum Sum Subarray uses them. Pairing them correctly is the test of whether you could state each one with the slide switched off.

47. Complete the line: Process: the crossing case, first attempt

Fill the middle

Fill in the blanks

From Process: the crossing case, first attempt — finish the line. Write what belongs on the right of the equals sign before you look.

\text\max_{\text{left piece}} + \max_{\text{right piece}} = ___

Why: Producing the right-hand side unprompted is the difference between recognising this line and being able to use it. That is the definition of crossing, so enumerate all such pairs and take the best.

48. Process: the crossing case, first attempt

Intuition

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

We split the array at the middle. The best subarray is on the left, on the right, or crossing the split. The first two are recursive calls. The crossing one is not.

Try every pair: one endpoint left of the split, one right of it

Why: That is the definition of crossing, so enumerate all such pairs and take the best. Correct, and easy to write.

It costs too much

Why: There are about n/2 choices on each side, so n squared over 4 pairs — quadratic work in the combine step alone. The recurrence becomes T(n) = 2T(n/2) + n squared, which unrolls to quadratic. The divide and conquer bought nothing.

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

Back up. The two halves of a crossing subarray are independent

Why: A crossing subarray is a left piece ending at the split, plus a right piece starting at the split. Maximize each separately with one scan apiece.

\[ \text{best crossing} = \max_{\text{left piece}} + \max_{\text{right piece}} \]

Two linear scans instead of a quadratic double loop. Notice the pivot came from re-reading the definition, not from a cleverer search.

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

49. Say it in words: Process: the crossing case, first attempt

Translation

\( \text{best crossing} = \max_{\text{left piece}} + \max_{\text{right piece}} \)

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.

50. Finding the crossing best without trying every pair

Intuition

A crossing subarray is built from two pieces glued together: a run ending exactly at the midpoint, and a run starting exactly at the next position.

Scan leftward from the midpoint, extending one step at a time and keeping the best running total — that finds the best possible left piece. Separately scan rightward from the next position the same way, for the best possible right piece. Add the two best pieces together.

\[ \text{best crossing} = \text{best suffix ending at midpoint} + \text{best prefix starting just after} \]

51. Plan first: Compute the crossing subarray on the running example

Step zero

Discussion prompt

Compute the crossing subarray on the running example — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Scan leftward from the midpoint to find the best suffix

Answer:

  1. Scan leftward from the midpoint to find the best suffix
  2. Scan rightward from just after the midpoint to find the best prefix
  3. Verify by adding the two best pieces and re-summing directly

52. Compute the crossing subarray on the running example

Worked example

Split our running example array A at its midpoint, between position 4 and position 5.

\[ \text{left half}: A[0..4] = [-2,1,-3,4,-1] \qquad \text{right half}: A[5..8] = [2,1,-5,4] \]

Scan leftward from the midpoint to find the best suffix

Why: Starting at position 4 and extending backward one step at a time, track the running total after each extension and keep the best one seen.

suffix starting atrunning total
4-1
33
20
11
0-1

Scan rightward from just after the midpoint to find the best prefix

Why: Starting at position 5 and extending forward one step at a time, track the running total after each extension and keep the best one seen.

prefix ending atrunning total
52
63
7-2
82

Verify by adding the two best pieces and re-summing directly

Why: The best suffix is 3 (positions 3 through 4), and the best prefix is 3 (positions 5 through 6), giving a crossing total of 6 for positions 3 through 6. Re-adding those four elements directly confirms the same total.

\[ 3 + 3 = 6; \quad A[3]+A[4]+A[5]+A[6] = 4-1+2+1 = 6\ \checkmark \]

53. Kadane works on a stream

Picture it

Animation

Shows: Kadane works on a stream — a rendered Manim animation.

Rendered with Manim.

Takeaway: Which the divide-and-conquer version cannot do.

54. The base case: a single element

Concept

Every recursive split eventually bottoms out at a piece with just one element. For a single element, there is only one possible subarray: the element by itself.

So the base case needs no comparison at all — the best subarray sum for a single element is simply that element's value. This is what stops the splitting.

55. Plan first: Recursion in action: splitting the left half again

Step zero

Discussion prompt

Recursion in action: splitting the left half again — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Solve the left-left piece, positions 0 through 2

Answer:

  1. Solve the left-left piece, positions 0 through 2
  2. Solve the right-left piece, positions 3 through 4
  3. Find this level's crossing case and combine
  4. Verify this matches the left-half answer used earlier

56. Recursion in action: splitting the left half again

Worked example

The left half found above, positions 0 through 4, is itself split in half and solved the same recursive way.

\[ A[0..4] = [-2,1,-3,4,-1] \ \Rightarrow\ \text{split into } A[0..2] \text{ and } A[3..4] \]

Solve the left-left piece, positions 0 through 2

Why: Checking every subarray of -2, 1, -3 by hand, the best is a single element: position 1 by itself, worth 1.

\[ \max\{-2,\ -1,\ -4,\ 1,\ -2,\ -3\} = 1 \]

Solve the right-left piece, positions 3 through 4

Why: Checking every subarray of 4, -1, the best is again a single element: position 3 by itself, worth 4.

\[ \max\{4,\ 3,\ -1\} = 4 \]

Find this level's crossing case and combine

Why: The best suffix ending at position 2 is -2 (positions 1 through 2), and the best prefix starting at position 3 is 4 (position 3 alone), giving a crossing total of 2 — worse than the right-left piece's 4.

\[ \text{crossing} = -2 + 4 = 2; \quad \max(1,\ 4,\ 2) = 4 \]

Verify this matches the left-half answer used earlier

Why: This recursive call reports 4 as the best subarray sum for positions 0 through 4 — exactly the left-half value that was plugged into the crossing computation on the previous slide.

\[ \text{left half } A[0..4] \text{ best} = 4\ \checkmark \]

57. The full divide-and-conquer combine

Worked example

Put all three cases together for the top-level split of the running example array.

Read off the left-half best

Why: Recursively solving positions 0 through 4 (shown on the previous slide) gives a best sum of 4.

\[ \text{left half best} = 4 \]

Read off the right-half best

Why: Checking every subarray of positions 5 through 8 (2, 1, -5, 4) by hand, the best is positions 5 through 6, worth 3.

\[ \text{right half best} = 3 \]

Read off the crossing best

Why: This was computed two slides ago: the best suffix (3) plus the best prefix (3) gives a crossing total of 6.

\[ \text{crossing best} = 6 \]

Verify the maximum of the three cases matches brute force

Why: Comparing 4, 3, and 6, the crossing case wins with 6, for positions 3 through 6. This is exactly the sum brute force found on the same array.

\[ \max(4, 3, 6) = 6\ \checkmark \]

58. The divide-and-conquer route

Picture it

Animation

Shows: The divide-and-conquer route — a rendered Manim animation.

Rendered with Manim.

Takeaway: The crossing case is the one people forget, and it is the interesting one.

59. Something is wrong here: forgetting the crossing case

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student assumes the best subarray must live entirely inside one half or the other, and only compares those two.

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

Correct: Taking the larger of 4 and 3 gives 4 as the reported answer.

Always compute the crossing case too, using the two-scan method, and compare all three candidates together.

Why: Taking the larger of 4 and 3 gives 4 as the reported answer. But the true best subarray, positions 3 through 6, straddles the boundary between the halves and was never considered.

60. Trap: forgetting the crossing case

Trap

The trap

A student assumes the best subarray must live entirely inside one half or the other, and only compares those two.

\[ \text{left half best} = 4, \quad \text{right half best} = 3 \]

Report the larger of just the two halves

Why: Taking the larger of 4 and 3 gives 4 as the reported answer. But the true best subarray, positions 3 through 6, straddles the boundary between the halves and was never considered.

\[ \max(4, 3) = 4 \quad (\text{wrong — misses the true best of } 6) \]

The fix

Always compute the crossing case too, using the two-scan method, and compare all three candidates together.

\[ \text{left half best} = 4, \quad \text{right half best} = 3, \quad \text{crossing best} = 6 \]

Compare all three cases, not just two

Why: Including the crossing case reveals the true winner: 6, at positions 3 through 6, which neither half alone could ever produce.

\[ \max(4, 3, 6) = 6\ \checkmark \]

61. Break it on purpose: forgetting the crossing case

Break the constraint

Discussion prompt

The rule this trap just fixed:

Including the crossing case reveals the true winner: 6, at positions 3 through 6, which neither half alone could ever produce.

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:

Taking the larger of 4 and 3 gives 4 as the reported answer. But the true best subarray, positions 3 through 6, straddles the boundary between the halves and was never considered.

62. Setting up the recurrence

Concept

Name the running time for an array of size n as a function, T of n. Solving the left half costs T of n over 2, and solving the right half costs another T of n over 2, since each half has about half the elements.

\[ T(n) = T\!\left(\tfrac{n}{2}\right) + T\!\left(\tfrac{n}{2}\right) + \text{(crossing work)} \]

The crossing work is the two linear scans — one leftward from the midpoint, one rightward — each visiting at most every element once, so together they cost a constant times n.

\[ T(n) = 2\,T\!\left(\tfrac{n}{2}\right) + \Theta(n) \]

63. The recursion tree, level by level

Intuition

At the very top level, the crossing work touches roughly n elements. One level down, there are two subproblems, each of half the size, so their crossing work is n over 2 plus n over 2 — n again, total.

Every level of the recursion, no matter how many pieces it has been split into, does about the same total amount of crossing work: proportional to n.

\[ \text{level work} \approx n \quad \text{at every level} \]

64. How deep does the recursion go

Intuition

Each level cuts the piece size in half. Starting from the running example's size of nine, the sizes shrink roughly like nine, then about four to five, then about two, then one — the base case.

The number of times you can cut a size in half before reaching one element is the base-two logarithm of the size. That is where the log n term comes from.

\[ \text{number of levels} \approx \log_2 n \]

65. Solving the recurrence

Concept

Multiply the work done per level, proportional to n, by the number of levels, proportional to log n.

\[ T(n) = 2\,T\!\left(\tfrac{n}{2}\right) + \Theta(n) \ \Longrightarrow\ T(n) = \Theta(n \log n) \]

This is a genuine improvement over the brute-force quadratic time, though it still requires the recursive splitting and the two extra scans at every level.

66. Kadane's Algorithm

Section

Section 4

67. What feels wrong about the quadratic scan?

Intuition

What feels wrong about this?

Brute force with the running-sum trick considers every start and every end:

\[ \Theta(n^{2}) \; \text{subarrays} \]

It is already much better than the three-loop version. It is still quadratic.

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

The feeling: when you slide the start one place to the right, you throw away everything you learned about the previous start and begin again from nothing.

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

So the question becomes: what could you carry forward from one position to the next? Answer that and the second loop disappears.

68. Kadane's idea: best subarray ending here

Concept

Instead of splitting the array, walk through it once, left to right, and at every position ask one question: what is the best subarray that ends exactly at this position?

best ending here — The sum of the best possible subarray whose last element is the current position — call this running quantity cur. The best answer overall is the largest value cur ever takes.

69. Take the definitions apart: subarray vs best ending here

Definition probe

Sort into buckets

Every line below is part of the definition of subarray or of best ending here — one or the other, never both. Put each where it belongs.

subarray
A contiguous slice of an array; a starting position and an ending position, plus every element in between.
best ending here
The sum of the best possible subarray whose last element is the current position; call this running quantity cur.; The best answer overall is the largest value cur ever takes.
b1
A contiguous slice of an array: a starting position and an ending position, plus every element in between. No gaps allowed.
b2
The sum of the best possible subarray whose last element is the current position — call this running quantity cur. The best answer overall is the largest value cur ever takes.

70. Extend or restart

Intuition

At each new position, the best subarray ending here has only two possible origins: either it extends the best subarray that ended at the previous position by tacking on this one new element, or the previous run was so damaging that it is better to restart fresh, using only this element by itself.

There is no third option — any subarray ending here either includes the position before it or it does not.

71. Why restarting is ever correct

Picture it

Animation

Shows: Why restarting is ever correct — a rendered Manim animation.

Rendered with Manim.

Takeaway: That single observation is the whole algorithm.

72. Complete the line: The move behind extend-or-restart, named

Fill the middle

Fill in the blanks

From The move behind extend-or-restart, named — finish the line. Write what belongs on the right of the equals sign before you look.

\text\max\big(\, \text{best}(i-1) + A[i], \;\; A[i] \,\big)(i) = ___

Why: Producing the right-hand side unprompted is the difference between recognising this line and being able to use it. You are standing at position i, building a subarray that ends here.

73. The move behind extend-or-restart, named

Concept

The move: #12 (Case-split on the last decision).

Ask what the last decision is

Why: You are standing at position i, building a subarray that ends here. The only decision left is whether this element joins the run that ended at i-1, or starts a fresh run of its own.

Write each case using a smaller answer

Why: Extend gives the best-ending-at-i-minus-1 plus this element. Restart gives just this element. Two cases, both in terms of something one step smaller.

\[ \text{best}(i) = \max\big(\, \text{best}(i-1) + A[i], \;\; A[i] \,\big) \]

Base case: nothing left to decide

Why: At position 0 there is no earlier run, so best(0) is A[0].

That recurrence was not discovered by cleverness. It fell out of naming the last decision. You are going to do exactly this, five more times, over the next three lessons.

74. Decode the notation: The move behind extend-or-restart, named

Notation

Annotate

From The move behind extend-or-restart, named — read this one piece at a time. What is each part doing?

On: \( \text{best}(i) = \max\big(\, \text{best}(i-1) + A[i], \;\; A[i] \,\big) \)

  • You are standing at position i, building a subarray that ends here. The only decision left is whether this element joins the run that ended at i-1, or starts a fresh run of its own.
  • Extend gives the best-ending-at-i-minus-1 plus this element. Restart gives just this element. Two cases, both in terms of something one step smaller.
  • At position 0 there is no earlier run, so best(0) is A[0].

75. The Last-Decision 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.

Step 1 is the whole thing. If you cannot name the last decision, you cannot write the recurrence — and no amount of staring at the array will produce one.

76. The one decision Kadane makes

Picture it

Animation

Shows: The one decision Kadane makes — a rendered Manim animation.

Rendered with Manim.

Takeaway: Every element poses exactly this question, and answering it is O(1).

77. Decision point: why does best-ending-here work when best-anywhere does not?

Intuition

What move should we make next?

Two candidate definitions for what the table entry means:

\[ (a) \;\; \text{best}(i) = \text{best subarray anywhere in } A[0 \ldots i] \]

\[ (b) \;\; \text{best}(i) = \text{best subarray ending exactly at } i \]

Only one of these lets you write a recurrence.

Pick one and say why the other one fails. This is a question about the definition, not about the code.

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

78. The Kadane recurrence, set up explicitly

Concept

Call the running best-ending-here value cur, and the best value seen at any point so far best. At each new position, update cur by comparing the extend option against the restart option.

\[ \text{cur} = \max\big(\,A[i],\ \ \text{cur}_{\text{previous}} + A[i]\,\big) \]

Then update the overall best by comparing it against the freshly updated cur.

\[ \text{best} = \max(\text{best},\ \text{cur}) \]

79. Kadane's algorithm in pseudocode

Concept

One pass, two variables, and a single decision repeated at every position: extend the run you are on, or abandon it and start fresh here.

KADANE(A)
  best = A[1]
  here = A[1]
  for i = 2 to A.length
    here = max(A[i], here + A[i])
    best = max(best, here)
  return best

Line 5 is the whole algorithm. If the run so far is dragging you below the current element on its own, drop it — a negative prefix can never help what comes after it. Line 6 just remembers the best answer seen.

80. Reading KADANE line by line

Notation

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

Annotate

  • here means: the best subarray that ENDS exactly at position i. Not the best anywhere — the best ending here.
  • best means: the best subarray seen anywhere so far. Keeping these two separate is the thing people get wrong.
  • Extend or restart. The left option starts fresh at i; the right option carries the previous run forward.
  • The restart wins exactly when here has gone negative, because a negative prefix only ever subtracts from what follows.
  • Record separately. A run can be the best-ending-here without being the best overall, so best never shrinks.
  • One pass, constant extra space. This is linear where brute force was cubic.

81. Step KADANE yourself

Invariant

After processing position i, here is the best sum ending exactly at i, and best is the best sum ending anywhere at or before i. Check both at every step.

Step through it

At each position, decide extend-or-restart yourself before looking at the answer.

  1. Line 2: start with here and best both at -2
  2. Line 5: 3 alone beats -2 plus 3, so restart
  3. Line 6: best is now 3
  4. Line 5: 3 plus -1 is 2, better than -1 alone, so extend
  5. Line 5: 2 plus 4 is 6, better than 4 alone, so extend
  6. Line 6: best is now 6
  7. Line 5: 6 minus 6 is 0, still better than -6 alone
  8. Line 5: 0 plus 2 is 2, and best stays at 6
  9. Line 7: the winning run was 3, -1, 4

82. Extend the run, or abandon it and start over

Picture it

Animation

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

Rendered with Manim.

Takeaway: At each position, extend the run or restart from here — whichever is larger. A negative running total is never worth carrying forward.

83. Why Kadane needs no recursion at all

Concept

Divide and conquer needed to look ahead at both halves before it could combine an answer. Kadane's rule only ever needs the single number cur from the position just before — nothing further back, and nothing from later in the array.

Because each step only depends on the step immediately before it, a single left-to-right pass is enough. No splitting, no recursion, no crossing case to worry about.

84. Kadane's invariant

Picture it

Animation

Shows: Kadane's invariant — a rendered Manim animation.

Rendered with Manim.

Takeaway: Two quantities, not one — conflating them is the usual bug.

85. What has to happen first: Full Kadane trace on the running example

Ranking

Put in order

Put the moves of Full Kadane trace on the running example into the order they have to happen.

  1. Initialize at position 0
  2. Apply extend-or-restart at every remaining position
  3. Verify the final best matches brute force and divide-and-conquer

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. With no previous position to extend from, cur must equal the first element itself, and best starts out equal to cur.

86. Full Kadane trace on the running example

Worked example

Walk the extend-or-restart rule across the entire running example array, one position at a time.

\[ A = [-2,\ 1,\ -3,\ 4,\ -1,\ 2,\ 1,\ -5,\ 4] \]

Initialize at position 0

Why: With no previous position to extend from, cur must equal the first element itself, and best starts out equal to cur.

iA[i]curbest
0-2-2-2

Apply extend-or-restart at every remaining position

Why: At each step, compare extending the previous cur by the new element against restarting at the new element alone, and take the larger; then update best if this new cur beats it.

iA[i]curbest
1111
2-3-21
3444
4-134
5255
6166
7-516
8456

Verify the final best matches brute force and divide-and-conquer

Why: The trace ends with best equal to 6, first reached at position 6, matching both earlier methods exactly.

\[ \text{best} = 6\ \checkmark \]

87. Full Kadane trace on the running example — line by line

Picture it

Animation

Shows: Each line of the worked example "Full Kadane trace on the running example", appearing one at a time.

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

Takeaway: The trace ends with best equal to 6, first reached at position 6, matching both earlier methods exactly.

88. Something is wrong here: resetting the running sum to zero

Anomaly

Predict first

A student writes this, and it looks reasonable:

A student writes cur as the larger of zero and the extended value, thinking of zero as a safe floor whenever the running total goes negative.

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

Correct: Since every element is negative, cur plus a negative element is always negative, so the comparison against zero always picks zero.

Never compare against zero. Only compare the element alone against the extended running total, exactly as the recurrence states.

Why: Since every element is negative, cur plus a negative element is always negative, so the comparison against zero always picks zero. Every single position ends up with cur equal to zero.

89. Trap: resetting the running sum to zero

Trap

The trap

A student writes cur as the larger of zero and the extended value, thinking of zero as a safe floor whenever the running total goes negative.

\[ \text{cur} = \max\big(0,\ \ \text{cur}_{\text{previous}} + A[i]\big) \]

Run this rule on the all-negative array C

Why: Since every element is negative, cur plus a negative element is always negative, so the comparison against zero always picks zero. Every single position ends up with cur equal to zero.

iA[i]cur (reset rule)
0-80
1-30
2-60
3-20
4-50
5-40

See the wrong final answer

Why: The best value never rises above zero, so this rule reports zero as the maximum subarray sum. But zero is not achievable by any non-empty subarray of an all-negative array — the rule has silently invented an empty subarray.

\[ \text{reported best} = 0 \quad (\text{wrong}) \]

The fix

Never compare against zero. Only compare the element alone against the extended running total, exactly as the recurrence states.

\[ \text{cur} = \max\big(A[i],\ \ \text{cur}_{\text{previous}} + A[i]\big) \]

Run the correct rule on the same array

Why: Without the zero floor, restarting still means starting fresh at the current negative element, not jumping to zero — so cur tracks real, achievable subarray sums at every position.

iA[i]cur (correct rule)
0-8-8
1-3-3
2-6-6
3-2-2
4-5-5
5-4-4

Verify the correct answer is the least negative element

Why: The largest value in the cur column is -2, at position 3, matching the brute-force answer on this same array exactly.

\[ \text{best} = -2\ \checkmark \]

90. What stays fixed: Trap: resetting the running sum to zero

Invariant

Step through it

Step through Trap: resetting the running sum to zero one row at a time. One of these columns never changes — find it, and say why it cannot.

  1. Step 1: i is 0
  2. Step 2: i is 1
  3. Step 3: i is 2
  4. Step 4: i is 3
  5. Step 5: i is 4
  6. Step 6: i is 5

91. Guess the shape of the answer: Kadane on the all-negative array, in full

Estimation

Predict first

Confirm the correct extend-or-restart rule end to end on the all-negative array, tracking best alongside cur.

Commit before you compute: what does Kadane on the all-negative array, in full 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 against the brute-force trace from earlier

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. Both methods report a best sum of -2, achieved by the single element at position 3 — full agreement, and the correct handling of an entirely negative input.

92. Kadane on the all-negative array, in full

Worked example

Confirm the correct extend-or-restart rule end to end on the all-negative array, tracking best alongside cur.

\[ C = [-8,\ -3,\ -6,\ -2,\ -5,\ -4] \]

Apply the correct rule at every position

Why: At each step, restarting always beats extending, since adding another negative number only makes the running total more negative than the fresh element alone.

iA[i]curbest
0-8-8-8
1-3-3-3
2-6-6-3
3-2-2-2
4-5-5-2
5-4-4-2

Verify against the brute-force trace from earlier

Why: Both methods report a best sum of -2, achieved by the single element at position 3 — full agreement, and the correct handling of an entirely negative input.

\[ \text{best} = -2\ \checkmark \]

93. Kadane's time complexity

Concept

The algorithm makes exactly one pass over the array, doing a constant amount of work — one comparison and one update — at each position.

\[ \Theta(n) \text{ time}, \quad \Theta(1) \text{ extra space} \]

No recursion, no extra arrays, no splitting. This is the fastest of the three approaches, and it is also the simplest to implement correctly.

94. Kadane's scan, one element at a time

Picture it

Animation

Shows: Kadane's scan, one element at a time — a rendered Manim animation.

Rendered with Manim.

Takeaway: One pass, one running total, one best-so-far.

95. Plan first: Reporting the start and end positions correctly

Step zero

Discussion prompt

Reporting the start and end positions correctly — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.

Hint: It starts with: Track a start-of-current-run marker alongside cur

Answer:

  1. Track a start-of-current-run marker alongside cur
  2. Only update the reported best-start and best-end when best actually improves
  3. Verify the final reported indices re-sum to the reported best

96. Reporting the start and end positions correctly

Worked example

Tracking the best sum is not enough — a full solution must also report which positions achieved it, without an off-by-one slip.

Track a start-of-current-run marker alongside cur

Why: Whenever cur restarts (the fresh element beats extending), move this marker to the current position. Whenever cur extends, leave the marker where it was.

iA[i]curstart of current run
0-2-20
1111
2-3-21
3443
4-133
5253
6163
7-513
8453

Only update the reported best-start and best-end when best actually improves

Why: If the reported end were updated on every single step regardless of whether best improved, it would drift forward past the true winning position — exactly the off-by-one trap seen earlier.

i where best improvedbestreported startreported end
0-200
1111
3433
5535
6636

Verify the final reported indices re-sum to the reported best

Why: The last row shows reported start 3, reported end 6, best 6. Re-adding A[3] through A[6] gives 4 minus 1 plus 2 plus 1, which is 6 — the indices and the sum agree exactly.

\[ A[3]+A[4]+A[5]+A[6] = 4-1+2+1 = 6\ \checkmark \]

97. Watch it run: Reporting the start and end positions correctly

Pattern

Step through it

Step through Reporting the start and end positions correctly one row at a time. What is driving the change, and what would the row after the last one be?

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

98. Correctness & the Big Picture

Section

Section 5

99. The non-empty subarray convention

Concept

By convention, the maximum sum subarray problem always requires picking at least one element. An empty selection is never a valid answer, even though its sum is trivially zero.

This convention is exactly why an all-negative array's correct answer is its least negative single element, never zero. Any method that quietly allows an empty subarray — like resetting a running sum to zero — will get this case wrong.

100. The two-dimensional version

Picture it

Animation

Shows: The two-dimensional version — a rendered Manim animation.

Rendered with Manim.

Takeaway: Reduce the new problem to the one you have already solved.

101. Guess the shape of the answer: Brute force confirms the answer on the full…

Estimation

Predict first

As a final cross-check, apply the straightforward brute-force scan to every starting position of the full nine-element array A.

Commit before you compute: what does Brute force confirms the answer on the full running example 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 overall winner matches both other methods

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. The largest value in the table is 6, from start position 3, matching the divide-and-conquer combine and the Kadane trace exactly, both of which also landed on positions 3 through 6.

102. Brute force confirms the answer on the full running example

Worked example

As a final cross-check, apply the straightforward brute-force scan to every starting position of the full nine-element array A.

Compute the best sum for each starting position

Why: For each start, extend the end marker across the rest of the array and keep the largest running sum reached.

startbest sum from this start
02
14
23
36
42
53
61
7-1
84

Verify the overall winner matches both other methods

Why: The largest value in the table is 6, from start position 3, matching the divide-and-conquer combine and the Kadane trace exactly, both of which also landed on positions 3 through 6.

\[ \max(2,4,3,6,2,3,1,-1,4) = 6\ \checkmark \]

103. Brute force confirms the answer on the full running… — line by line

Picture it

Animation

Shows: Each line of the worked example "Brute force confirms the answer on the full running example", appearing one at a time.

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

Takeaway: The largest value in the table is 6, from start position 3, matching the divide-and-conquer combine and the Kadane trace exactly, both of which also landed on positions 3 through 6.

104. All three methods agree

Intuition

Brute force, divide-and-conquer, and Kadane's algorithm all found the same subarray, positions 3 through 6, with the same sum.

methodbest sum foundpositions
brute force63 to 6
divide and conquer63 to 6
Kadane's algorithm63 to 6

That agreement is not a coincidence — it is a correctness check. Any time you write a fast algorithm, checking it against a slow, obviously-correct brute force on the same input is exactly how you catch bugs.

105. Fill in: positions for All three methods agree

Comparison

Comparison matrix

From All three methods agree: refill the positions column from what you know. The rest of the table is as it appeared.

methodbest sum foundpositions
brute force63 to 6
divide and conquer63 to 6
Kadane's algorithm63 to 6

106. Decision point: three algorithms, one problem

Intuition

What move should we make next?

Same answers, three running times:

\[ \Theta(n^{2}) \qquad \Theta(n \log n) \qquad \Theta(n) \]

For each of the three, name the move or moves that produced its running time. You have the tools to account for all three.

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

107. Teach it back: Decision point: three algorithms, one problem

Explain it

Discussion prompt

Explain Decision point: three algorithms, one problem 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:

For each of the three, name the move or moves that produced its running time. You have the tools to account for all three.

108. Comparing the three approaches

Concept

Same problem, same answer, three very different running times.

approachtime complexitycore idea
brute forcequadratictry every start and end, running sum
divide and conquern log nsplit, solve each half, add the crossing case
Kadane's algorithmlinearone pass, extend or restart at each step

109. Fill in: time complexity for Comparing the three approaches

Comparison

Comparison matrix

From Comparing the three approaches: refill the time complexity column from what you know. The rest of the table is as it appeared.

approachtime complexitycore idea
brute forcequadratictry every start and end, running sum
divide and conquern log nsplit, solve each half, add the crossing case
Kadane's algorithmlinearone pass, extend or restart at each step

110. When would you actually pick each one?

Intuition

Brute force is worth writing first anyway — it is nearly impossible to get wrong, and it is the correctness baseline everything else gets checked against.

Divide and conquer is worth understanding because the same left-half, right-half, crossing-case pattern reappears in many other problems throughout this course. In production code for this specific problem, though, Kadane's linear scan wins outright — it is both the fastest and the simplest to implement.

111. By analogy: When would you actually pick each one?

Analogy

Discussion prompt

Explain When would you actually pick each one? 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:

Brute force is worth writing first anyway — it is nearly impossible to get wrong, and it is the correctness baseline everything else gets checked against.

112. The unifying recipe

Pattern

1. Pin down what counts as a valid answer

Why: Confirm the answer must be a contiguous, non-empty run — not any subset of elements, and not an empty selection worth zero.

2. Start with brute force for a correctness baseline

Why: Try every candidate directly, using a running sum instead of resumming from scratch. It is slow but unmistakably correct.

3. Look for optimal substructure to speed it up

Why: Split the problem and enumerate every place the answer could hide — here, entirely left, entirely right, or crossing the split. Never skip the crossing case.

4. Look for a single left-to-right recurrence

Why: If each answer only depends on the one immediately before it, a single pass replaces the whole recursive structure — extend or restart, no splitting needed.

5. Stress-test the edge cases before trusting the code

Why: Check an all-negative input, and re-sum the reported start and end positions directly to catch any off-by-one drift.

113. Where this shows up: Maximum Sum Subarray

Real world

Discussion prompt

Outside this lesson: where does Maximum Sum Subarray 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 unifying recipe 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:

The maximum sum subarray problem worked three ways: brute force, divide-and-conquer (with the crossing subarray case), and Kadane's linear scan. Targets confusing a subarray with a subsequence, forgetting the crossing case in divide-and-conquer, resetting Kadane's running sum to zero on an all-negative array, and off-by-one errors when reporting the start/end indices.

114. Reporting WHERE, not just how much

Picture it

Animation

Shows: Reporting WHERE, not just how much — a rendered Manim animation.

Rendered with Manim.

Takeaway: Two extra variables turn a value into an answer.

115. What has to happen first: Applying the recipe to a brand-new array

Ranking

Put in order

Put the moves of Applying the recipe to a brand-new array into the order they have to happen.

  1. Run Kadane's extend-or-restart rule position by position
  2. Read off the winning positions
  3. Verify by re-adding those five elements 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. At each step, compare extending the previous cur by the new element against restarting fresh at the new element alone.

116. Applying the recipe to a brand-new array

Worked example

Try the recipe on an array we have not seen before, to confirm it transfers.

\[ D = [\,3,\ -2,\ 5,\ -1,\ 6,\ -3\,] \]

Run Kadane's extend-or-restart rule position by position

Why: At each step, compare extending the previous cur by the new element against restarting fresh at the new element alone.

iD[i]curbest
0333
1-213
2566
3-156
461111
5-3811

Read off the winning positions

Why: Best first reaches 11 at position 4, and tracking the restart marker shows the run has been extending continuously since position 0 — so the winning subarray spans positions 0 through 4.

\[ \text{positions 0 through 4}: 3,-2,5,-1,6 \]

Verify by re-adding those five elements directly

Why: 3 plus negative 2 plus 5 plus negative 1 plus 6 equals 11, exactly matching the reported best — the algorithm and a direct check agree.

\[ 3-2+5-1+6 = 11\ \checkmark \]

117. Applying the recipe to a brand-new array — line by line

Picture it

Animation

Shows: Each line of the worked example "Applying the recipe to a brand-new array", appearing one at a time.

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

Takeaway: 3 plus negative 2 plus 5 plus negative 1 plus 6 equals 11, exactly matching the reported best — the algorithm and a direct check agree.

118. Rule out three: Check yourself: subarray or subsequence?

Elimination

Eliminate the wrong options

Which of these is a valid subarray of A?

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. Positions 3 through 6: 4, -1, 2, 1
  • B. Positions 0, 3, 5, and 8: -2, 4, 2, 4
  • C. Positions 3, 4, and 6, skipping position 5: 4, -1, 1
  • D. The empty selection, with sum 0

Survives elimination: A

Why: A subarray must be contiguous, with no skipped positions in between the start and the end. Positions 3 through 6 include every index in that range, so it qualifies. The others skip indices (making them subsequences, not subarrays) or select nothing at all, which is excluded since the problem requires a non-empty answer.

119. Check yourself: subarray or subsequence?

Check

Recall the running example array and its structure.

\[ A = [-2,\ 1,\ -3,\ 4,\ -1,\ 2,\ 1,\ -5,\ 4] \]

Check your understanding

Which of these is a valid subarray of A?

  • A. Positions 3 through 6: 4, -1, 2, 1 (correct)
  • B. Positions 0, 3, 5, and 8: -2, 4, 2, 4
  • C. Positions 3, 4, and 6, skipping position 5: 4, -1, 1
  • D. The empty selection, with sum 0

Answer: A

Why: A subarray must be contiguous, with no skipped positions in between the start and the end. Positions 3 through 6 include every index in that range, so it qualifies. The others skip indices (making them subsequences, not subarrays) or select nothing at all, which is excluded since the problem requires a non-empty answer.

Why B tempts people
This picks isolated positions 0, 3, 5, and 8 while skipping everything in between. Skipping positions makes it a subsequence, not a subarray, even though its sum happens to be positive.
Why C tempts people
This looks contiguous at a glance but deliberately skips position 5 in the middle of the range. Any gap at all disqualifies a selection from being a subarray.
Why D tempts people
The problem requires selecting at least one element. An empty selection is excluded by convention, precisely so that all-negative arrays cannot cheat their way to a sum of zero.

120. Answer it before you see the options: Check yourself: the crossing case

Prediction

Predict first

In divide and conquer, what goes wrong if you only compare the best subarray of the left half to the best subarray of the right half?

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: You might miss the true best subarray if it straddles the midpoint

Why: On the running example, the true best subarray spans positions 3 through 6 with a sum of 6, straddling the boundary between the halves. Comparing only the whole-half maximums (4 and 3) would report 4 and completely miss the crossing subarray's larger sum.

121. Check yourself: the crossing case

Check

Think back to the divide-and-conquer approach and the trap about skipping a case.

Check your understanding

In divide and conquer, what goes wrong if you only compare the best subarray of the left half to the best subarray of the right half?

  • A. You might miss the true best subarray if it straddles the midpoint (correct)
  • B. Nothing — the two halves always contain the true best subarray between them
  • C. The algorithm's running time becomes cubic instead of quadratic
  • D. The recursion would never reach its base case

Answer: A

Why: On the running example, the true best subarray spans positions 3 through 6 with a sum of 6, straddling the boundary between the halves. Comparing only the whole-half maximums (4 and 3) would report 4 and completely miss the crossing subarray's larger sum.

Why B tempts people
This is exactly the mistake in the trap slide: the crossing subarray, by definition, cannot live entirely inside either half, so the two half-maximums alone can miss it.
Why C tempts people
This confuses a correctness bug with a performance concern. Skipping the crossing case makes the algorithm wrong, not slower — omitting it would actually make it slightly faster, and wrong.
Why D tempts people
The base case is reached whenever a piece shrinks to a single element, which happens regardless of whether the crossing case is computed at higher levels.

122. How sure are you: Check yourself: Kadane on all-negative input

Commit first

Predict first

Running the correct Kadane recurrence (no comparison against zero) on array C, what is the maximum subarray sum?

Commit to an answer, then rate it — certain, fairly sure, or guessing — and write the rating down before you turn the page.

Correct: -2, the least negative single element

Why: Since every element is negative, extending never helps, so the correct rule restarts at every position and cur equals that position's own value. The largest of those values is -2, at position 3, matching the brute-force trace on the same array exactly.

The rating matters as much as the answer: confident-and-wrong is the combination that survives revision, because nothing about it feels like it needs revisiting.

123. Check yourself: Kadane on all-negative input

Check

Recall the all-negative example array and the correct, non-resetting extend-or-restart rule.

\[ C = [-8,\ -3,\ -6,\ -2,\ -5,\ -4] \]

Check your understanding

Running the correct Kadane recurrence (no comparison against zero) on array C, what is the maximum subarray sum?

  • A. -2, the least negative single element (correct)
  • B. 0, treating an empty subarray as allowed
  • C. -8, the first element in the array
  • D. -28, the sum of every element in the array

Answer: A

Why: Since every element is negative, extending never helps, so the correct rule restarts at every position and cur equals that position's own value. The largest of those values is -2, at position 3, matching the brute-force trace on the same array exactly.

Why B tempts people
This is the reset-to-zero bug from the trap slide: comparing the running sum against zero silently allows an empty subarray, which is not a valid non-empty answer.
Why C tempts people
This picks the first element out of habit without comparing it against the rest of the array. Position 3's value of -2 is less negative and therefore larger.
Why D tempts people
This sums the entire array as one subarray instead of searching for the best possible contiguous piece, and also computes the total incorrectly along the way.

124. Answer it before you see the options: Check yourself: reporting indices

Prediction

Predict first

Which implementation mistake would cause the reported end position to be one step past the true answer?

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: Updating the reported end position every iteration, instead of only when best actually improves

Why: In the trace, cur kept extending through positions 7 and 8 even though best had already stopped improving after position 6. Updating the reported end on every iteration, rather than only inside the branch where best improves, drags the reported end forward past the true winning position.

125. Check yourself: reporting indices

Check

A student's Kadane implementation reports the winning subarray as starting at position 3 and ending at position 7, but the correct answer ends at position 6.

Check your understanding

Which implementation mistake would cause the reported end position to be one step past the true answer?

  • A. Updating the reported end position every iteration, instead of only when best actually improves (correct)
  • B. Never moving the start-of-current-run marker when the running sum restarts
  • C. Comparing the extend and restart values with the inequality reversed
  • D. Using zero-indexed positions consistently throughout the trace

Answer: A

Why: In the trace, cur kept extending through positions 7 and 8 even though best had already stopped improving after position 6. Updating the reported end on every iteration, rather than only inside the branch where best improves, drags the reported end forward past the true winning position.

Why B tempts people
This would cause the reported start position to be wrong (stuck at an old value), not the end position drifting one step too far.
Why C tempts people
Flipping the inequality would change which value wins at each step, likely breaking the whole trace, but it does not specifically explain an end position that is exactly one step past the correct one.
Why D tempts people
Consistent zero-indexing by itself does not cause this specific drift; the bug here is about when the reported end gets updated, not which indexing convention is used.

126. Rule out three: Check yourself: comparing running times

Elimination

Eliminate the wrong options

Ranking the three approaches from slowest to fastest for large n, which order is correct?

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. Brute force, then divide and conquer, then Kadane's algorithm
  • B. Kadane's algorithm, then divide and conquer, then brute force
  • C. Divide and conquer, then brute force, then Kadane's algorithm
  • D. All three run in the same amount of time for large n

Survives elimination: A

Why: Brute force is quadratic, divide and conquer is n log n, and Kadane's algorithm is linear. For large n, quadratic grows fastest, n log n grows slower than that, and linear grows slowest of all — so that is the slowest-to-fastest order.

127. Check yourself: comparing running times

Check

Think about how each approach's running time grows as the array size n gets very large.

Check your understanding

Ranking the three approaches from slowest to fastest for large n, which order is correct?

  • A. Brute force, then divide and conquer, then Kadane's algorithm (correct)
  • B. Kadane's algorithm, then divide and conquer, then brute force
  • C. Divide and conquer, then brute force, then Kadane's algorithm
  • D. All three run in the same amount of time for large n

Answer: A

Why: Brute force is quadratic, divide and conquer is n log n, and Kadane's algorithm is linear. For large n, quadratic grows fastest, n log n grows slower than that, and linear grows slowest of all — so that is the slowest-to-fastest order.

Why B tempts people
This reverses the true order entirely. Kadane's linear scan is the fastest of the three, not the slowest.
Why C tempts people
This swaps brute force and divide and conquer. Quadratic time (brute force) grows faster than n log n time (divide and conquer) as n increases.
Why D tempts people
Quadratic, n log n, and linear are three genuinely different growth rates; for a large enough n they produce very different running times, as the earlier hundred-thousand-element example showed.

128. Toolkit update

Concept

Moves added today:

Moves you reused today:

Move #12 will run the next three lessons. Every dynamic programming recurrence you write from here is this move applied to a different last decision.

Full toolkit so far: #1 through #12.

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

129. Break it if you can: Toolkit update

Counterexample

Discussion prompt

Move #12 will run the next three lessons. Every dynamic programming recurrence you write from here is this move applied to a different last decision.

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.

130. Connect it up: Maximum Sum Subarray

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — The Problem · Brute Force · Divide and Conquer · Kadane's Algorithm · Correctness & the Big Picture. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

131. What you can do now

Recap

You solved one problem three ways and confirmed all three agree.

TechniqueThe one move
Brute forcerunning sum instead of resumming
Divide and conquerleft, right, AND crossing
Kadane's algorithmextend or restart, no reset to zero
Reporting indicesre-sum the reported range to check it

Sources

  1. Cormen, Leiserson, Rivest, Stein — Introduction to Algorithms, 3rd ed., Ch. 4 (Divide-and-Conquer, the maximum-subarray problem) — MIT Press, 2009.
  2. Bentley — Programming Pearls, 2nd ed., Column 8 (the maximum subarray problem and the linear-time scanning algorithm) — Addison-Wesley, 1999.
  3. All brute-force, divide-and-conquer, and Kadane traces on the running example arrays recomputed by hand and cross-checked against each other for agreement. — 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