4.4 Algorithm Formats: Natural Language, Flowcharts, Pseudocode, and Tracing
Key Takeaways
- An algorithm is a finite, ordered sequence of unambiguous steps that solves a problem; the same algorithm can be written in natural language, as a flowchart, or in pseudocode.
- In standard flowchart notation, ovals mark start and end, parallelograms mark input and output, rectangles mark processing steps, and diamonds mark decisions; on the test, use the symbol legend ETS provides.
- A trace table has one column for each variable, plus columns for conditions and output, and one row for each step that changes something.
- Sequencing errors include initializing a variable inside a loop, using a value before computing it, and updating a counter in the wrong place.
- A pre-test loop (while) can run zero times; a post-test loop (do … while, repeat … until) always runs at least once.
What this competency asks
ETS asks you to understand how to develop and analyze algorithms expressed in multiple formats (natural language, flowcharts, and pseudocode):
- Interpret diagrams that describe algorithms, given an explanation of the symbols used.
- Compare algorithms written in multiple formats.
- Trace and analyze algorithms written in different formats.
- Identify the correct sequencing of steps in an algorithm, and errors in sequencing.
Three ways to express one algorithm
An algorithm is a finite sequence of unambiguous steps that solves a problem. Here is one algorithm, finding the largest value in a list, in three formats.
Natural language
- Take the first value as the largest so far.
- Look at each remaining value in turn.
- If a value is greater than the largest so far, it becomes the new largest.
- When no values remain, report the largest so far.
Pseudocode (ETS notation)
int findMax ( int[ ] nums, int n )
int largest ← nums[0]
for ( int i ← 1; i < n; i ← i + 1 )
if ( nums[i] > largest )
largest ← nums[i]
end if
end for
return largest
end findMax
Flowchart: see the diagram in this section.
| Format | Strength | Weakness |
|---|---|---|
| Natural language | Easy for anyone to read | Ambiguous ("look at each value": in what order? what if the list is empty?) |
| Flowchart | Shows branching and looping visually | Bulky for long algorithms; hard to edit |
| Pseudocode | Precise and compact; close to real code | Requires knowing the notation |
When a question asks you to compare formats, check that each version has the same initialization, the same condition (for example, > versus ≥), the same loop range, and the same result. A natural-language version that says "start with 0 as the largest" is not equivalent to one that starts with the first element, because it fails when every value is negative.
Reading flowcharts
ETS's content description says you will interpret diagrams given an explanation of the symbols used, so read the legend on the test. The conventional symbols (from the ISO 5807 flowchart standard) are:
| Symbol | Name | Meaning |
|---|---|---|
| Oval (rounded) | Terminator | Start or end |
| Parallelogram | Input/output | Read input or display output |
| Rectangle | Process | Assignment or calculation |
| Diamond | Decision | A yes/no or true/false test with two labeled exits |
| Arrow | Flow line | Order of execution |
A loop in a flowchart is an arrow that returns to an earlier decision. When you trace one, keep going around the loop until the decision sends you out.
Tracing: the most important skill
Tracing (also called desk checking) means executing an algorithm by hand. Build a trace table:
- One column per variable, plus columns for any condition and for output.
- Write the initial values.
- Go line by line. When a variable changes, add a row with its new value.
- Record
trueorfalsefor each condition so you follow the correct branch. - Stop when the loop condition fails, and note the final values.
Worked trace
int total ← 0
int k ← 1
while ( k ≤ 4 )
if ( k % 2 == 0 )
total ← total + k * 3
else
total ← total + k
end if
k ← k + 1
end while
print "Result: " + total
| k (at test) | k ≤ 4 | k % 2 == 0 | total after body | k after body |
|---|---|---|---|---|
| 1 | true | false | 0 + 1 = 1 | 2 |
| 2 | true | true | 1 + 6 = 7 | 3 |
| 3 | true | false | 7 + 3 = 10 | 4 |
| 4 | true | true | 10 + 12 = 22 | 5 |
| 5 | false | — | exit | — |
Output: Result: 22. Notice that the loop test runs five times but the body runs four times. Questions about "how many times is the condition evaluated" versus "how many times does the body execute" test exactly this difference.
Sequencing: order matters
Many questions show an algorithm with steps missing or out of order. Common sequencing errors:
| Error | Example | Symptom |
|---|---|---|
| Initialization inside the loop | sum ← 0 placed inside the for loop | The sum is reset every pass; only the last value survives |
| Using a value before it is computed | Printing average before average ← sum / n | Prints a stale or default value |
| Updating the counter in the wrong place | i ← i + 1 placed before list[i] is used | Skips the first element and may run past the end |
| Missing update | No k ← k + 1 in a while loop | Infinite loop |
| Return too early | return inside the loop body | Only the first element is checked |
Example of the first error:
int sum ← 0
for ( int i ← 0; i < n; i ← i + 1 )
sum ← 0
sum ← sum + list[i]
end for
Deleting the sum ← 0 inside the loop fixes the algorithm, because the initialization above the loop already runs once. To find a sequencing error, trace a small input, such as a list of two or three values, and compare with the intended result.
Loops that test at the top vs. the bottom
A pre-test loop (while) checks its condition before each pass, so it may run zero times. A post-test loop (do … while, repeat … until) checks after each pass, so it runs at least once. This matters with sentinel values, special values such as −1 that mark the end of input:
// Version A: pre-test
int record ← readNext ( )
while ( record ≠ -1 )
process ( record )
record ← readNext ( )
end while
// Version B: post-test
int record ← 0
repeat
record ← readNext ( )
process ( record )
until ( record == -1 )
Version A reads first and tests before processing, so the sentinel is never processed, and an input that starts with −1 processes nothing. Version B processes whatever it just read before testing, so it always calls process ( -1 ) once at the end, including when the input is empty.
Other defects that tracing reveals
- Off-by-one errors:
i ≤ ninstead ofi < nin a 0-based array of length n reaches the invalid index n. - Infinite loops: the condition can never become false. For example,
while ( count ≠ 10 )withcountstarting at 1 and increasing by 2 skips 10 forever. - Unreachable code: statements after an unconditional
return, or inside anifwhose condition can never be true, such as( x > 10 ) and ( x < 5 ).
Consider the following pseudocode segment.
What is printed?int val ← 1
for ( int outer ← 1; outer ≤ 3; outer ← outer + 1 )
for ( int inner ← 1; inner ≤ outer; inner ← inner + 1 )
val ← val + outer * inner
end for
end for
print val
A flowchart legend states that parallelograms represent input or output, rectangles represent processing, and diamonds represent decisions. An algorithm asks a user to enter a test score, checks whether the score is at least 70, and displays "Pass" or "Fail". Which shapes represent entering the score and checking the score?
Two versions of an algorithm read values until the sentinel −1 appears. Version A reads a value before a while ( record ≠ -1 ) loop and reads again at the end of the loop body. Version B uses repeat with the read and process ( record ) inside the body and until ( record == -1 ) at the bottom. How do their behaviors differ?