Did you know that a program can give the wrong maximum value even if every comparison it makes is mathematically correct? A common beginner’s mistake is starting the ‘largest so far’ variable at zero. That works for positive lists, but for [−8, −3, −11] it incorrectly returns zero, a number which does not even appear in the list. Algorithm design is about specifying the problem, choosing an invariant that stays true during the loop, handling unusual inputs and testing the result—not merely typing Python quickly.
This independent Ministry of Education V3.0 reference teaches 2027 G3 Computing K349 Algorithm Design, focusing on manual list algorithms: finding minimum and maximum without min()/max(), calculating total and mean without sum(), searching positions in lists and strings without index()/find(), extracting items meeting a condition, splitting a string by a delimiter without split(), and developing solutions incrementally. It includes traceable code, twenty-six original worked cases, a twelve-stage Specify–Trace–Test protocol, an eduKate three-pupil workshop and independent challenges.
The sample records, names and identifiers below are fictional educational data. No real student grades or passwords are needed. The code deliberately shows explicit loops, because the syllabus tests understanding of the underlying algorithms; using a built-in function may be good practice in professional software but may fail to meet a school question that explicitly prohibits it.
A 50-second learning route
- State what the algorithm should return: first position, all positions, smallest value, mean, extracted items or text fields.
- Choose the proper starting state: for maximum/minimum initialise from a real first item, or define an empty-list policy.
- Make one pass through the data and update only when the specified condition is met.
- Trace each iteration by hand and predict the final output before running the code.
- Test normal, boundary and error cases, then solve a new variant without relying on the previous data.
1. The Difference Between Writing Code and Designing an Algorithm
An algorithm is a finite, precise series of steps to solve a problem under stated inputs. It can be described by natural language, a flowchart or code. A computer executes the chosen instructions, not the developer’s intention. If a student writes an incorrect initial condition, Python faithfully produces a result based on that mistake. Algorithmic literacy therefore begins with a clear contract: what counts as valid input, what output is required, and what should happen at special boundaries.
2. Exact 2027 G3 Computing K349 Requirements
The official 2027 G3 Computing K349 syllabus, Module 2.5 Algorithm Design, especially 2.5.1, explicitly requires algorithms for finding minima and maxima without min()/max(), sum and average without sum(), searching item or character positions without index()/find(), extracting items based on criteria, and splitting strings by a delimiter without split(). It also requires modular and incremental development, manual small-instance reasoning and adapting familiar algorithms to new problems. The syllabus does not list bubble sort, merge sort or binary search as compulsory named algorithms in these outcomes; they should not be imposed as mandatory topics simply because they appear in other computing courses.
3. Every Search Question Has a Hidden Contract
If a list contains the target twice, does the question ask for the first occurrence, the last occurrence, all indices or simply whether the target exists? These are distinct algorithms even though they all compare items against the same target. For a list [4,7,4], a first-index search for 4 returns 0, while an all-index search returns [0,2]. A student must specify zero-based indexing and missing-item behaviour.
4. Minimum and Maximum Require Real Initial Values
For a nonempty numeric list, initialise the candidate from the first element. Then compare each later item and replace the candidate when a more extreme value appears. This creates a useful invariant: after processing the first k values, the candidate equals the maximum or minimum of those k values. For an empty list there is no first element, so the function must reject it or return an explicitly documented special result.
5. Totals and Means Are Accumulations
To calculate a list’s total without sum(), start an accumulator at zero and add each value exactly once. For the mean, count how many numeric items were included and divide total by count after completing the scan. A nonempty-input condition is required before division. Unlike maximum, starting a total at zero is correct for negative numbers because zero is the addition identity, not an assumed candidate from the dataset.
6. Searching Characters Is Similar to Searching List Items
Python strings have indexed characters. A manual search can loop over positions and compare each character with a target. The algorithm must decide whether to return the first position or collect all. The built-in .find() method is convenient in ordinary code, but a ‘without find’ syllabus question requires the explicit comparison sequence.
7. Extraction Changes Selection, Not Ordering Automatically
A filter that selects values meeting a criterion should usually preserve the original order unless the problem says otherwise. Selecting positive values from [0, −4, 6, 2, −1] gives [6,2]; it does not require sorting or replacing negative values with zero. A criterion such as greater-than-or-equal to 10 also differs from strictly greater than 10 at the boundary.
8. Manual String Splitting Demands Boundary Decisions
A delimiter such as a comma marks field boundaries. A manual split scans each character, accumulates the current field, appends the field when it meets the delimiter, and appends the final field after the loop. Two consecutive delimiters can produce an empty field, and a trailing delimiter can do the same. Whether to retain empty fields depends on the declared specification.
9. Eight Rooms of Algorithmic Reasoning
Room one defines valid input and output. Room two handles empty or missing cases. Room three chooses an initial state. Room four states the loop’s invariant. Room five selects the comparison and update. Room six traces at least one pass by hand. Room seven tests duplicates and boundaries. Room eight transfers the logic to unfamiliar data without using a forbidden convenience method.
10. Twelve-Stage Specify–Trace–Test Protocol
Stage 1 — Write the input contract
Specify list of numbers, character sequence or delimited text. For minimum and mean, decide what the algorithm should do if the input is empty.
Stage 2 — Write the expected output contract
Should the function return a numeric result, an index, a list of indices, a filtered list or a list of text fields? Do not postpone this choice until the code is half written.
Stage 3 — Solve a three-item case by hand
Use values such as [−8,−3,−11]. Identify the largest or matching positions manually and record the comparisons needed.
Stage 4 — Choose safe initial variables
For a maximum, use values[0] after checking nonempty input. For a total, use zero. For a search collecting all matches, start an empty output list.
Stage 5 — Decide the exact iteration range
For a max where first item is the initial candidate, compare items from index one. For a search that might match at index zero, inspect the whole list.
Stage 6 — Write the update condition
Use greater-than for a new maximum, less-than for minimum and equality for an exact match. A misplaced equals sign can change duplicate or boundary behaviour.
Stage 7 — Trace the invariant
At each loop step, explain what the candidate or output list represents. This gives a correctness reason rather than only a final number.
Stage 8 — Handle special cases deliberately
Empty list, all-negative list, repeated targets, target absent, delimiter at start and end, and no delimiter all deserve individual checks.
Stage 9 — Implement with permitted Python features
Use variables, for/while, conditions, strings and lists from the syllabus. Follow a question’s restriction against min, max, sum, index, find or split.
Stage 10 — Compare predicted and observed results
Compute the expected answer before executing. If they differ, use a trace table or selected print statements to locate the earliest state that diverged.
Stage 11 — Explain efficiency at a suitable level
One full pass inspects up to n values in a list of length n; describing that work is reasonable, but advanced complexity theory is not required by the named Module 2.5 outcomes.
Stage 12 — Adapt the algorithm to another task
Change largest to smallest, first match to all matches, or comma to pipe delimiter. The student should explain precisely which lines and invariants change.
11. Original Fictional Dataset: The Equipment Return Log
Consider a fictional equipment room recording items returned each day: [−2, −7, −1, −4] could represent signed corrections to a running balance, rather than actual negative counts of children. If a function is asked for the highest value, it should return −1; a zero initial candidate would be wrong. A different list [12, 9, 12, 7, 12] provides three occurrences of the target 12 at indices 0, 2 and 4, showing why a first-match algorithm differs from an all-match algorithm.
The original text record ‘A01,B03,,C02,’ includes two empty fields under a literal comma-splitting contract, one between consecutive commas and one after the trailing comma. The expected output is [‘A01′,’B03′,”,’C02’,”]. This is a deliberately constructed input so pupils can test their own manual splitter without disclosing personal information.
12. Five Working Python Algorithm Cores
Maximum of a Nonempty List
def manual_max(values):
if len(values) == 0:
return None
best = values[0]
for item in values[1:]:
if item > best:
best = item
return best
print(manual_max([-8, -3, -11])) # -3
The invariant is that best is the greatest value already inspected. Beginning at values[0] protects all-negative lists; returning None for empty input is a deliberate teaching contract, not a compulsory choice for every problem.
Manual Total and Mean
def manual_mean(values):
if len(values) == 0:
return None
total = 0
count = 0
for item in values:
total = total + item
count = count + 1
return total / count
print(manual_mean([4, 7, 10])) # 7.0
Each item is added exactly once, and count tracks the number of inspected items. Separating addition and division makes it easier to identify a loop that accidentally changes count twice.
Search for Every Matching Position
def all_positions(values, target):
answer = []
for i in range(len(values)):
if values[i] == target:
answer.append(i)
return answer
print(all_positions([12, 9, 12, 7, 12], 12)) # [0, 2, 4]
The output grows whenever an index matches the target. It does not stop after the first match and it returns an empty list for no matches; this is not the same contract as returning −1 for a failed first-index search.
Extract Values Satisfying a Criterion
def positive_items(values):
answer = []
for item in values:
if item > 0:
answer.append(item)
return answer
print(positive_items([0, -4, 6, 2, -1])) # [6, 2]
The condition is strictly greater than zero; a zero does not qualify. Original order is retained because answer.append(item) follows the input sequence.
Split a String Manually on a Delimiter
def manual_split(text, delimiter):
if len(delimiter) != 1:
return None
fields = []
current = ""
for char in text:
if char == delimiter:
fields.append(current)
current = ""
else:
current = current + char
fields.append(current)
return fields
print(manual_split("A01,B03,,C02,", ","))
# ['A01', 'B03', '', 'C02', '']
The function intentionally requires one delimiter character, retains empty fields, and appends the last field. A full production parser would need a more complex contract for quoted commas or multi-character delimiters; neither is assumed here.
13. Twenty-Six Worked Algorithmic Cases
Worked Example A — A — Maximum with positive values
Scan [2, 7, 4]. Initialise best to 2, update to 7 and retain 7. The maximum is 7. Starting at a real first item gives the same result for positive and negative lists.
Worked Example B — B — Maximum with negatives
For [−8,−3,−11], initialise best=−8, then update to −3 and keep it. A zero initial candidate incorrectly reports a value absent from the input.
Worked Example C — C — Minimum with positive values
Scan [9,5,7], starting best=9. Update to 5, then keep it. The result 5 is the minimum of the actual list, not a default assumption.
Worked Example D — D — Minimum with negatives
For [−4,−12,−6], best starts at −4 and becomes −12. Comparing signed numbers correctly means −12 is smaller than −6.
Worked Example E — E — Single-item list
For [42], manual maximum and minimum both return 42 without further comparisons. This is a valid nonempty boundary case.
Worked Example F — F — Empty-list policy
For [], there is no real minimum or maximum. In the supplied teaching code the function returns None. Another specification might require an error, but the choice must be explicit.
Worked Example G — G — Total including negatives
For [5,−3,2], start total=0, then total becomes 5, 2, 4. Zero is a correct additive identity even though it might be an incorrect starting maximum.
Worked Example H — H — Mean of three values
The list [4,7,10] has total 21 and count 3, so mean 7. Averages must divide by actual item count, not by a guessed capacity.
Worked Example I — I — Mean of an empty input
Dividing total zero by item count zero is undefined. Reject or specially report an empty list before division rather than create a runtime failure without explanation.
Worked Example J — J — First search match
In [8,3,8], the first index of target 8 is 0. A first-match search can return immediately at that point, but such an early return would be wrong for an all-occurrences task.
Worked Example K — K — All search matches
In the same [8,3,8], all matching zero-based indices are [0,2]. The output contract dictates continued scanning after the first hit.
Worked Example L — L — No match
Searching [5,4,3] for 9 yields no matching positions. The all-positions function returns [], while a separate first-index design might return −1.
Worked Example M — M — Target at the last index
For [1,3,5,7], target 7 occurs at index 3. A range ending one step too early will miss it, making this an essential boundary test.
Worked Example N — N — String character search
The string ‘banana’ contains ‘a’ at indices 1,3,5. A manual character scan should return all three if the task asks for every occurrence.
Worked Example O — O — Search is case-sensitive by default
In the string ‘AaA’, an exact comparison against ‘a’ matches only index 1. Convert case deliberately only if the specification requires case-insensitive matching.
Worked Example P — P — Extract positive values
The criterion >0 applied to [0,−4,6,2,−1] produces [6,2]. The zero is excluded because the inequality is strict.
Worked Example Q — Q — Extract at least a threshold
With threshold 10, values [10,9,11] produce [10,11] for condition >=10. A boundary value of 10 distinguishes this from >10.
Worked Example R — R — Preserve order when filtering
From [4,8,2,10], keeping even numbers yields the same order [4,8,2,10]. Filtering alone does not sort, even though the list appears untidy.
Worked Example S — S — Extract characters
From ‘A2B3′, keeping only digit characters under a stated digit criterion yields ’23’. Building the new string one accepted character at a time avoids relying on .split() or indexing helpers.
Worked Example T — T — Delimiter at the start
Splitting ‘,A’ on commas with empty fields preserved gives [”, ‘A’]. Omitting the initial empty field changes the chosen contract.
Worked Example U — U — Delimiter at the end
Splitting ‘A,’ produces [‘A’,”]; forgetting the final fields.append(current) drops the trailing empty field.
Worked Example V — V — Two delimiters together
Splitting ‘A,,B’ gives [‘A’,”,’B’]. The empty middle field is a deliberate consequence of consecutive separators.
Worked Example W — W — No delimiter
Splitting ‘ABC’ on comma produces [‘ABC’], a one-element list. The absence of a delimiter is not an error under the declared contract.
Worked Example X — X — Counting repeated matches
The list [12,9,12,7,12] has three matches at [0,2,4]. The index list’s length is three, which can be counted without confusing count with location.
Worked Example Y — Y — Trace a loop’s invariants
A running sum should equal the total of items processed so far. After reading first two values [3,−1], it should be 2. Any trace row showing 4 identifies an earlier wrong update.
Worked Example Z — Z — A modular redesign
A fictional tool must return all temperatures above 30. Reuse the filtered-items approach with the criterion >30, test 30 as a boundary, and preserve original observation order.
14. Why One-By-One Search Is Not the Same as Binary Search
Linear search inspects elements in sequence until a match or the end. The K349 algorithm outcome explicitly requires this kind of manual item or character search, without index() or find(). Binary search relies on a sorted sequence and additional interval logic; it may be an interesting enrichment topic but is not named as a compulsory algorithm in the 2027 Module 2.5.1 listing. Students should meet the actual specification before learning optional alternatives.
15. Two Bad Initialisations and Their Repairs
For maximum, beginning at zero is unsafe because all items may be negative. For minimum, beginning at a huge made-up sentinel is also unnecessary when the real first element is available. Starting from values[0] establishes a candidate that belongs to the dataset. A running total is different: zero is a mathematically valid identity for addition. Teaching the reason for each initial value is more transferable than memorising one template for every algorithm.
16. Trace Tables Are Evidence of Correctness
Make columns for iteration number, item or index read, comparison result and updated candidate or output. Trace a four-item case and explain what remains true after each step. For the max example [−8,−3,−11], start best −8, update at −3 and keep −3 at −11. If a pupil records an incorrect candidate during the trace, that is the first repair node; changing the final answer without fixing the process will not transfer.
17. Why the Manual Split Algorithm Needs a Final Append
Each delimiter causes the accumulated current field to be stored and cleared. The last field has no following delimiter in the ordinary case, so it must be appended after the loop. An end delimiter instead leaves an empty current string which is also appended under the chosen contract. This simple detail is an excellent test of whether a student has truly traced a loop rather than guessed the expected output.
18. Normal, Boundary and Error Cases
- For a maximum: a mixed-sign list, a single item, all-negative items and empty input.
- For a search: target first, target last, repeated target, no target and an empty sequence.
- For a manual split: one delimiter, none, adjacent delimiters, leading/trailing delimiters and an invalid delimiter length.
- For extraction: item exactly on the comparison threshold, immediately below, immediately above, and no qualifying items.
- For mean: one numeric item, multiple signed items and empty input before dividing.
19. A Ninety-Minute Three-Pupil Tutorial Route
- 10 minutes: retrieval of loop variables, index positions and valid-input rules.
- 15 minutes: manually derive maximum and total using short lists, deliberately including all-negative numbers.
- 20 minutes: write and trace the maximum, average and all-positions functions without banned convenience calls.
- 20 minutes: rotate three students through filter, character search and manual splitting cases.
- 15 minutes: individually solve a fresh no-match or boundary-case problem with a predicted trace and output.
- 10 minutes: identify the first incorrect state, repair it and explain a changed independent input.
20. eduKate Depth–Load–Transfer Diagnostic
Depth: Can the learner explain why max starts from the first element but sum starts from zero? Load: Can they manage loop indices, repeated matches, delimiter boundaries and validation without mixing output contracts? Transfer: Can they change a first-position search into an all-position search for an unfamiliar string? The Clementi three-pupil small-group standard guides diagnosis, repair, practice and independent proof.
21. Misconception Ledger
- Initialising maximum at zero works for all lists: false when all numbers are negative.
- A found item always means stop: false when the contract requests every match.
- An index and an item value are interchangeable: false; a list position and stored entry are distinct.
- Filtering automatically sorts: false; iteration preserves order unless separate sorting is specified.
- A manual split may omit its final field: false under the specified retain-all-fields contract.
- Passing one ordinary input is proof of correctness: false; special cases and input validity remain untested.
22. Parent and Tutor Progress Questions
Ask the pupil to state the expected return before writing code; ask what the candidate represents after each iteration; ask which banned method the question excludes; and ask how the function behaves with no data. A student who can explain one new test without being told the answer shows more genuine understanding than one who types an earlier memorised Python block correctly.
23. Frequently Asked Questions
Why not use Python’s max() or min()?
The 2027 K349 algorithm-design outcome explicitly asks students to know how to obtain extrema without those built-ins in specified tasks.
Should maximum start at zero?
Not generally. Initialise from the first actual item of a nonempty list so negative-only data are handled.
What about an empty list?
A maximum or mean is undefined for an empty list; decide whether to return a documented special value or reject the input.
Does the syllabus require bubble sort?
The named K349 Module 2.5.1 outcomes focus on extrema, totals, searching, extraction and splitting; bubble sort is not listed as a compulsory named algorithm there.
How do I find every repeated match?
Scan every index and append positions which match the target; do not return after the first match.
Why is Python indexing important?
Python uses zero-based list and string indexing in these examples; the first position is 0, not 1.
Can the search return −1 on failure?
Yes, for an explicitly specified first-index contract; an all-indices contract can reasonably return an empty list instead.
Why append after the splitting loop?
The final field otherwise disappears when no delimiter follows it.
Is filtering the same as sorting?
No. Filtering selects items by a criterion, usually preserving their original order.
What is the best proof of learning?
An independently written and traced solution for a new set of input, boundary and error conditions.
24. Independent Depth and Transfer Practice
Practice 1 — All-negative maximum
Write a maximum algorithm for [−11,−4,−9] without max(); show candidate updates and result −4.
Practice 2 — Manual mean
Use an explicit accumulator and count to find the mean of [−2,4,7,3], which is 3, without sum().
Practice 3 — First vs every occurrence
Search [5,3,5,5] for 5; design first match 0 and all indices [0,2,3], then explain why the loops differ.
Practice 4 — Find characters
Scan ‘assessment’ for ‘s’ and return every zero-based index. Check that repeated letters are not lost.
Practice 5 — Extract at a boundary
From [9,10,11,0] keep values at least 10. Explain why 10 qualifies but 9 does not.
Practice 6 — Manual delimiter split
For ‘A,,B,’ and comma delimiter, expect [‘A’,”,’B’,”]; explain each append action.
Practice 7 — Empty policy
Decide an explicit safe result for calculating a mean of [], justify it and show that division by zero does not occur.
Practice 8 — Independent transfer problem
Design a function returning all days with rainfall above a chosen nonnegative threshold. Define type validation, ordering, equality and the empty-result behaviour before coding.
25. Closing Principle
Good algorithm design begins before code: define the input and result, choose an invariant, initialise safely, scan deliberately, and test the boundaries. Small loops can reveal the whole reasoning structure of a useful program. Independent understanding is evident when a pupil can modify one of these algorithms for a new problem without leaning on a copied worked example.
Helpful Reading and Official Sources
Read Reference 325 — Python Functions, Reference 310 — Strings, Lists and Files, Reference 203 — Computational Thinking, the MOE V3.0 library and the Clementi teaching standard. Confirm the examined algorithms in 2027 G3 Computing K349, Module 2.5, page 8. For pedagogical consultation questions, use eduKate’s main enquiry route without assuming any particular course has available places.
Reference Navigation
Previous: Reference 341 · MOE V3.0 Library.
