CSPIQ · AP Computer Science Principles · Lesson 12 of 25
CSPIQ · AP Computer Science Principles

Lesson 12: Developing Algorithms & List Traversals

Big Idea 3 (AAP) · Phase 3

Objectives

Warm-Up

Without running it, what question does this code answer?

mystery ← 0
FOR EACH t IN temps
{
    IF (t > 90)
    {
        mystery ← mystery + 1
    }
}
DISPLAY (mystery)

Read the shape: a counter starting at 0, bumped by 1 whenever an element passes a test. It answers: "How many temperatures exceed 90?"

Experienced programmers don't trace this — they recognize it. There are about five list-processing shapes, recombined endlessly on the exam. This lesson teaches you all five, first as recognizable silhouettes, then as code you could write blind.


Core Concept

Algorithms and the three building blocks

An algorithm is a finite set of instructions that accomplishes a task. The CED's foundational claim: every algorithm can be built from three constructs:

You've now learned all three. Everything from here — searches, simulations, your Create PT — is these three, composed. Also CED-official: algorithms can be expressed in natural language, flowcharts/diagrams, pseudocode, or code, and the same algorithm can be implemented in different ways; conversely, different algorithms can solve the same problem.

The Big Five list algorithms

1. Sum — accumulator starts at 0, adds every element:

sum ← 0
FOR EACH x IN nums
{
    sum ← sum + x
}

2. Average — sum, then divide by the count after the loop:

sum ← 0
FOR EACH x IN nums
{
    sum ← sum + x
}
avg ← sum / LENGTH(nums)

The division lives outside the loop. Inside-the-loop division is a classic broken variant (it computes garbage — a running "average of averages").

3. Count-matches — counter starts at 0, +1 per element passing a test (the warm-up).

4. Maximum — a champion variable, challenged by every element:

max ← nums[1]
FOR EACH x IN nums
{
    IF (x > max)
    {
        max ← x
    }
}

Initialization is the soul of this one: start max at the first element, not at 0. With max ← 0 and an all-negative list, the code reports 0 — a number not even in the list. Minimum: flip the comparison to x < min. Which brings a general rule: the comparison's direction tells you max (>) vs. min (<); the initialization tells you whether it's honest.

5. Linear search — walk the list; flag when found:

found ← false
FOR EACH x IN roster
{
    IF (x = target)
    {
        found ← true
    }
}

Variants track where (an index counter) or count occurrences. Linear search checks elements one by one, in order — on average more work for longer lists, and it's the only search that works on unsorted data. (Binary search, next lesson, is the fast one — with a prerequisite.)

Reading an unfamiliar algorithm

When the shape isn't instantly familiar, extract three facts:

  1. Initialization: what value does the result variable start at? (0 → probably sum/count; first element → max/min; true/false → a search flag)
  2. Update rule: what changes it, and under what condition?
  3. Post-processing: anything after the loop (a division, a comparison)?

Those three facts identify the algorithm more reliably than reading it line by line — and they're exactly where the exam plants bugs.

Modifying algorithms — the exam's favorite twist

"What happens if line 3 changes to X?" Approach: identify which of the three facts changed.

count ← 0
FOR EACH x IN nums
{
    IF (x ≥ 10)
    {
        count ← count + 1
    }
}

Change to >: elements exactly 10 stop counting (boundary shift). Change count ← count + 1 to count ← count + x: it becomes a conditional sum — total of the big values, not a tally. Change count ← 0 to count ← 1: everything over-reports by one. Small edits, categorical changes — name the new algorithm, don't re-trace from scratch.


Worked Examples

Example 1 (easy): Name that algorithm

Problem: What does this compute?

r ← scores[1]
FOR EACH s IN scores
{
    IF (s < r)
    {
        r ← s
    }
}
DISPLAY (r)

Strategy: Three facts. Init: first element. Update: replace when smaller. Post: none.

Solution: Minimum of the list. The < points down; honest initialization from the list itself.

Example 2 (medium): The broken maximum

Problem: A student writes a maximum-finder but initializes with max ← 0. For which list does it fail, and what does it output?

(i) [3, 9, 2] (ii) [−4, −1, −7]

Solution: (i): 3 > 0, then 9 > 3 → max = 9 ✓ (works by luck — positives exceed 0). (ii): no element beats 0 → max stays 0, but the true maximum is −1. The code reports a value that isn't in the list.

Interpretation: max ← 0 works exactly until the data goes non-positive — a bug that hides during testing with "nice" data (Lesson 2's boundary lesson, again). The honest fix: max ← nums[1].

Example 3 (medium): Trace a search variant

Problem: What is displayed?

names ← ["Ann", "Raj", "Lu", "Raj"]
spot ← 0
i ← 0
FOR EACH n IN names
{
    i ← i + 1
    IF (n = "Raj")
    {
        spot ← i
    }
}
DISPLAY (spot)

Solution:

n i spot
"Ann" 1 0
"Raj" 2 2
"Lu" 3 2
"Raj" 4 4

Displays 4 — the position of the last match, because later matches overwrite spot.

Interpretation: This search keeps updating on every hit. Finding the first match requires protection (only assign when spot = 0). "First or last occurrence?" is a real exam discrimination — check whether later matches can overwrite.

Example 4 (AP-style): Assemble the algorithm

Problem: A program must display the average of the values in data that are greater than 50. Which is correct?

(A)

sum ← 0
FOR EACH d IN data
{
    IF (d > 50) { sum ← sum + d }
}
DISPLAY (sum / LENGTH(data))

(B)

sum ← 0
count ← 0
FOR EACH d IN data
{
    IF (d > 50)
    {
        sum ← sum + d
        count ← count + 1
    }
}
DISPLAY (sum / count)

(C)

sum ← 0
count ← 0
FOR EACH d IN data
{
    sum ← sum + d
    count ← count + 1
}
DISPLAY (sum / count)

(D)

count ← 0
FOR EACH d IN data
{
    IF (d > 50) { count ← count + 1 }
}
DISPLAY (count / LENGTH(data))

Solution: (B). Averaging a subset needs the subset's sum AND the subset's count — both gated by the same IF. (A) divides the subset's sum by the count of everything (too small a result). (C) averages the whole list, filter forgotten. (D) computes the fraction of elements over 50 — a different question.

Interpretation: Test with tiny data: [60, 40]. Should be 60. (A): 60/2 = 30 ✗. (B): 60/1 = 60 ✓. A two-element test case beats staring.


Common Mistakes

  1. Initializing max/min to 0. Fails on all-negative (max) or all-positive (min) data. Initialize from the list: nums[1].
  2. Dividing inside the loop. The average's division happens once, after accumulation completes.
  3. Subset average with a full-list denominator. Filtered sums need filtered counts — Example 4's exact trap.
  4. First vs. last occurrence. An unguarded search assignment keeps overwriting → reports the last match. If the question wants the first, look for the guard.
  5. Recognizing the silhouette but skipping the boundary. > 50 vs. ≥ 50 in the filter still matters — silhouette identifies the algorithm, boundary reading gets the actual point.

Practice Problems

Question 1

What does this compute?

z ← 0
FOR EACH v IN vals
{
    z ← z + v
}
DISPLAY (z / LENGTH(vals))
Question 2
In problem 1's code, what does z hold immediately after the loop (before the DISPLAY)?
Question 3

Which initialization makes this a correct maximum-finder for ANY non-empty list?

m ← ____
FOR EACH x IN nums
{
    IF (x > m) { m ← x }
}
Question 4

What is displayed?

hits ← 0
FOR EACH g IN [70, 85, 90, 85]
{
    IF (g ≥ 85)
    {
        hits ← hits + 1
    }
}
DISPLAY (hits)
Question 5
Changing problem 4's condition from g ≥ 85 to g > 85 changes the display to:
Question 6

What does this display for nums ← [5, 2, 8, 1]?

out ← nums[1]
FOR EACH x IN nums
{
    IF (x > out) { out ← x }
}
DISPLAY (out)
Question 7
A linear search for a value NOT in the list will:
Question 8

What is displayed?

w ← ["kiwi", "fig", "plum", "fig"]
first ← 0
i ← 0
FOR EACH x IN w
{
    i ← i + 1
    IF (x = "fig" AND first = 0)
    {
        first ← i
    }
}
DISPLAY (first)
Question 9

Which question does this code answer?

flag ← false
FOR EACH p IN prices
{
    IF (p > 100)
    {
        flag ← true
    }
}
Question 10
(Select two answers.) An algorithm must find the average of a list. Which statements are true?
Question 11
To convert the code in problem 9 into one that checks whether ALL prices are over 100, which rewrite works?

12 (short response, WR 2(a) style). Write pseudocode that displays how many values in list donations are between 10 and 20 inclusive. Then state what your code displays for donations ← [10, 25, 15, 9, 20].


Create PT Connection

The Big Five are, quite literally, a menu of Create PT algorithms. Every one satisfies the rubric's demands — iteration over your list, selection making a real decision, sequencing throughout — and each has a natural user story:

Algorithm PT program it powers
Count-matches "How many workouts this month hit my goal?"
Max/min "Which expense was my biggest?"
Filtered average "My quiz average, ignoring dropped scores"
Linear search "Is this ingredient in my allergy list?"

Pick one, wrap it in the procedure template from Lesson 11's PT section, and your program's core exists. The differentiation between a 4 and a 6 on the PT isn't algorithmic cleverness — it's that you can explain your algorithm precisely in the written responses. You now own that vocabulary: initialization, update rule, post-processing, boundary choice.


Show answer key & explanations

(g) Answer Key

1. (B). Sum accumulated, divided by length after the loop = average — the canonical shape.

2. (A). Before the division, z is the raw sum. The average only exists at the DISPLAY. Reading when in the code a question asks about is a graded skill.

3. (C). Initialize from the data itself — correct for negatives, positives, everything non-empty. (A) fails all-negative lists (Example 2); (B) fails lists topping out below 1; (D) confuses a length with a value.

4. (B). ≥ 85 catches 85, 90, 85 → 3. (A) is the strict-comparison count — see problem 5.

5. (C). Strict > 85 keeps only 90 → 1. One symbol, two points apart — the boundary pair (4,5) is exactly how the exam tests it.

6. (D). Champion pattern: 5, then 8 wins, 1 doesn't → 8. (C) = 16 is the "added instead of compared" silhouette-confusion answer.

7. (B). No match → flag never flips; every element gets examined (FOR EACH doesn't quit early). Not an error — "not found" is a valid, useful outcome.

8. (C). The guard first = 0 locks in the FIRST match: fig at position 2; the fig at position 4 can't overwrite. Compare Example 3 (unguarded → last). Guard spotted = answer known.

9. (B). One hit flips the flag forever → an "at least one" (existence) check. (A) would need the all-pattern — see problem 11.

10. (A) and (C). (A): representation changes, algorithm doesn't — CED-official. (C): the correct average shape. (B): many implementations can be correct (different loops, different variable names). (D): plain averages need no IF at all.

11. (A). The all-pattern: assume true, let any counterexample destroy it. One element ≤ 100 → flag false forever. (B) reports "at least one price ≤ 100" — the opposite claim. (D) resets the flag every iteration, so only the last element's verdict survives — a subtle, classic bug worth staring at for ten seconds.

12. (Model answer.)

count ← 0
FOR EACH d IN donations
{
    IF (d ≥ 10 AND d ≤ 20)
    {
        count ← count + 1
    }
}
DISPLAY (count)

For [10, 25, 15, 9, 20]: 10 ✓ (inclusive), 25 ✗, 15 ✓, 9 ✗, 20 ✓ (inclusive) → displays 3. Full credit requires inclusive comparisons on both ends and the correct trace.

Answer letter distribution check: B, A, C, B, C, D, B, C, B, A+C, A — singles: A×2, B×4, C×4, D×1 + multi (A,C). Cumulative through L12 ≈ A 24%, B 31%, C 25%, D 19% — D-debt persists; L13's key is D-weighted by design.


Exam tip: When an algorithm question appears, say the silhouette's name to yourself before reading the choices ("this is a filtered count") — then verify the three facts: initialization, update rule, post-processing. Naming first prevents the distractors from talking you out of what you saw.

← All lessons
Lesson 13 ›
Score: 0/0 correct