Computer Science 9618/41 — October/November 2024
Cambridge A-Level · Practical · worked solutions for every part, with the mark scheme
Topics Programming Paradigms (Procedural and Object-oriented) · File Processing and Exception Handling · Algorithms and Abstract Data Types
A program sorts string data using different sorting methods.
Open the evidence document, evidence.doc
Make sure that your name, centre number and candidate number appear on every page of this document. This document must contain your answers to each question.
Save this evidence document in your work area as:
evidence_ followed by your centre number_candidate number, for example: evidence_zz999_9999
A class declaration can be used to declare a record. If the programming language used does not support arrays, a list can be used instead.
One source file is used to answer Question 1. The file is called Data.txt
The text file Data.txt stores string data items. Each data item is on a new line in the text file.
The function ReadData():
- has a local array of strings that can store 45 items
- reads each line of data and stores it in the array
- returns the array.
Write program code for the function ReadData().
Save your program as Question1_N24.
Copy and paste the program code into part 1(a) in the evidence document.
Answer
def ReadData():
DataArray = [""] * 45
Index = 0
with open("Data.txt", "r") as FileHandle:
for Line in FileHandle:
DataArray[Index] = Line.strip()
Index += 1
return DataArray
See program code
Background Concept
A sequential text file stores data one item after another, in order. To read all items, a program opens the file and processes each line until the file ends. In Python, a list can be used where the task describes an array. A list such as [""] * 45 creates space for 45 string items, which matches the requirement exactly.
A function should return the populated data structure so that the main program or another function can use the values later. For file data like this, it is also important to remove the end-of-line character from each line before storing it.
Understanding the Question
You are told that Data.txt contains one string per line, and that ReadData() must:
- have a local array that can store 45 items
- read each line from the file
- store each line in that array
- return the array
So this is not just a file-reading fragment: it must be a complete function definition that creates the array locally, fills it, and returns it.
Approach
The simplest approach is:
- Create a local list with 45 string slots.
- Set an index variable to the first position.
- Open
Data.txtfor reading. - Read each line in turn.
- Remove the newline from the line and store the text in the next array position.
- Increase the index.
- Return the filled list.
Using a with open(...) statement is a clean Python way to open and close the file automatically.
Step-by-Step Reasoning
DataArray = [""] * 45
creates a list with 45 positions. That satisfies the requirement for a local array that can store 45 strings.
Index = 0
starts the position counter at the first Python index. Python lists are 0-indexed, so the 45 items go into positions 0 to 44.
with open("Data.txt", "r") as FileHandle:
opens the source file for reading.
for Line in FileHandle:
reads one line at a time until the file finishes. Because each data item is on a new line, each loop iteration gives one string item.
DataArray[Index] = Line.strip()
stores the current line in the next array position. strip() removes the newline character, so the stored value is just the word itself, such as beige rather than beige\n.
Index += 1
moves to the next array position ready for the next item.
return DataArray
sends the whole filled array back to the caller.
This works correctly for the given file because Data.txt contains exactly 45 lines.
Key Takeaways
- A function can read a file and return a complete array/list of values.
- For line-based text files, each line usually needs trimming before storage.
- In Python, a list is the practical equivalent of the array required by the task.
- Keeping a separate index is a standard way to fill a fixed-size array from a file.
Common Mistakes
- Forgetting to remove the newline character, which causes untidy output later.
- Appending into a list without first creating the required 45-item local array when the question explicitly asks for one.
- Starting the index at
1in Python, which would leave position0unused and risk going out of range. - Not returning the array at the end of the function.
Things to Be Careful About
- The file name must match exactly:
Data.txt. - Python list positions are
0to44, not1to45. - The function must be named
ReadData()exactly as required. - If you use
strip(), that is fine here because the file items are single words with no intended leading or trailing spaces.
The function FormatArray() takes an array of strings as a parameter. It concatenates the contents of the array into one string with a space between each array element. The function returns the concatenated string.
Write program code for FormatArray().
Save your program.
Copy and paste the program code into part 1(b)(i) in the evidence document.
Answer
def FormatArray(DataArray):
OutputString = DataArray[0]
for Index in range(1, len(DataArray)):
OutputString = OutputString + " " + DataArray[Index]
return OutputString
See program code
Background Concept
Concatenation means joining strings together. When a question asks for array contents to be combined into a single string with separators, the normal pattern is:
- start with an initial string
- loop through the remaining items
- add the separator and the next item each time
This avoids having an extra unwanted separator at the start or end.
Understanding the Question
FormatArray() receives an array of strings and must return one string containing all array items with a space between each one. It does not print the result itself; it returns the finished string so another part of the program can output it.
Approach
A clean way to do this is:
- Start the result with the first element.
- Loop from the second element to the end.
- Add a space and then the current element.
- Return the completed string.
This method guarantees exactly one space between items and no extra space at the beginning.
Step-by-Step Reasoning
def FormatArray(DataArray):
defines a function that accepts the array as a parameter.
OutputString = DataArray[0]
starts the result with the first word in the array. That means the first item is added without a leading space.
for Index in range(1, len(DataArray)):
loops through every remaining position, starting at index 1.
OutputString = OutputString + " " + DataArray[Index]
adds one space followed by the next string item.
return OutputString
returns the final combined string.
For example, if the array began with beige, green, scarlet, then after the first few steps the result would become:
beigebeige greenbeige green scarlet
and so on until all 45 items are included.
Key Takeaways
- Use an accumulator variable to build a long string gradually.
- Start with the first element when you want separators only between items.
- Functions often return formatted data instead of displaying it directly.
Common Mistakes
- Starting with an empty string and always adding
" " + item, which creates an unwanted leading space. - Forgetting to return the final string.
- Printing inside the function instead of returning the string.
Things to Be Careful About
- The parameter name can vary, but it must represent the incoming array.
- The function should work for the full array, not only a few hard-coded positions.
- Keep the function focused on formatting only; output belongs in the main program.
The main program:
- calls
ReadData()and stores the returned array - calls
FormatArray()with the returned array and outputs the returned string.
Write program code for the main program.
Save your program.
Copy and paste the program code into part 1(b)(ii) in the evidence document.
Answer
DataArray = ReadData()
OutputString = FormatArray(DataArray)
print(OutputString)
See program code
Background Concept
The main program coordinates the overall task by calling functions in the correct order. A function call may return a value, and that returned value can be stored in a variable and then passed into another function.
This is a standard procedural design style: each function does one job, and the main program links them together.
Understanding the Question
The main program must:
- call
ReadData()and store the returned array - call
FormatArray()using that array - output the string returned by
FormatArray()
So the answer is not a full program rewrite; it is the sequence of statements needed in the main program.
Approach
The dependency order matters:
- Read the data first.
- Store it in an array variable.
- Pass that array to the formatting function.
- Store the returned string.
- Print the string.
Each step uses the result of the previous step.
Step-by-Step Reasoning
DataArray = ReadData()
calls the file-reading function and stores the returned array of 45 strings.
OutputString = FormatArray(DataArray)
passes that array into FormatArray(), which builds one space-separated string and returns it.
print(OutputString)
displays the final string on screen.
This satisfies all three bullets in the question exactly.
Key Takeaways
- The main program should call functions in a logical order.
- Returned values should be stored when they are needed later.
- Passing the output of one stage into the next is a core procedural programming skill.
Common Mistakes
- Calling
FormatArray()before the data has been read. - Forgetting to store the returned array from
ReadData(). - Printing the array directly instead of printing the formatted string.
Things to Be Careful About
- Use the function names exactly as given:
ReadData()andFormatArray(). FormatArray()needs the array as an argument.- The final output should be the returned string, not the raw Python list representation.
Test your program.
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 1(b)(iii) in the evidence document.
Answer
Input: none
Expected output:
beige green scarlet silver bronze slate yellow orange jade lavender magnolia magenta turquoise black grey russet maroon mango mint purple red pink white cream navy olive brown violet cyan amber aqua azure copper fawn fuschia gold indigo ivory mauve mulberry peach periwinkle plum rose sage
beige green scarlet silver bronze slate yellow orange jade lavender magnolia magenta turquoise black grey russet maroon mango mint purple red pink white cream navy olive brown violet cyan amber aqua azure copper fawn fuschia gold indigo ivory mauve mulberry peach periwinkle plum rose sage
Background Concept
A test run for a file-processing program checks that the program reads the data correctly and that later processing stages produce the required output format. Here, no keyboard input is needed because all data comes from Data.txt.
Understanding the Question
After writing ReadData() and FormatArray(), you must run the program and capture the output. Since the data is read in file order and simply formatted with spaces, the expected output is the 45 words from Data.txt on one line in exactly the same order.
Approach
To work out the expected result:
- Read the values from
Data.txtin their original order. - Keep the order unchanged, because no sorting has been done yet.
- Place a single space between each item.
- Show the final one-line output.
Step-by-Step Reasoning
ReadData() stores the lines from the file in this order:
- beige
- green
- scarlet
- silver
- bronze
- slate
- yellow
- orange
- jade
- lavender
- magnolia
- magenta
- turquoise
- black
- grey
- russet
- maroon
- mango
- mint
- purple
- red
- pink
- white
- cream
- navy
- olive
- brown
- violet
- cyan
- amber
- aqua
- azure
- copper
- fawn
- fuschia
- gold
- indigo
- ivory
- mauve
- mulberry
- peach
- periwinkle
- plum
- rose
- sage
FormatArray() then joins them into one string with spaces between each item. Because there is no sorting in part (b), the order stays exactly the same as the file.
Key Takeaways
- When no processing changes the order, expected output matches the file sequence.
- Testing output is often just careful tracing of earlier code.
- A screenshot part still depends on understanding what the code should display.
Common Mistakes
- Accidentally showing Python list brackets and quotes instead of the formatted string.
- Changing the order of items even though no sort has been called yet.
- Missing one of the 45 values when checking the output.
Things to Be Careful About
- The output is one space-separated line.
- There is no user input for this test.
fuschiamust be spelled exactly as it appears in the file, even if it looks unusual.
The function CompareStrings():
- takes two strings as parameters
- compares each string, one character at a time, to identify which string comes first alphabetically. If the first two characters are the same, the second character of each string is compared. This continues until the two characters are different.
The function:
- returns 1 if the first parameter comes before the second alphabetically
- returns 2 if the second parameter comes before the first alphabetically.
Write program code for CompareStrings().
Assume that all strings are in lower case.
Assume that a difference between two strings will always be identified before the end of one string is reached.
Do not use an in-built string comparison function.
The strings must be compared one character at a time.
Save your program.
Copy and paste the program code into part 1(c) in the evidence document.
Answer
def CompareStrings(String1, String2):
Position = 0
while String1[Position] == String2[Position]:
Position += 1
if ord(String1[Position]) < ord(String2[Position]):
return 1
else:
return 2
See program code
Background Concept
Alphabetical comparison can be done lexicographically: compare the first characters of both strings; if they are the same, compare the next characters; continue until a difference is found. The string with the smaller character at the first differing position comes first alphabetically.
The question specifically forbids using a built-in whole-string comparison, so the program must inspect one character position at a time.
Understanding the Question
CompareStrings() takes two strings and must return:
1if the first string should come before the second alphabetically2if the second string should come before the first
You are told that all strings are lower case and that a difference will be found before one string ends. That removes the need to handle case conversion or one-string-is-a-prefix-of-the-other cases.
Approach
The standard method is:
- Start at position
0. - While both characters at that position are the same, move to the next position.
- When the first different pair is found, compare those two characters.
- Return
1or2based on which character is earlier alphabetically.
Using ord() makes the character comparison explicit by comparing character codes.
Step-by-Step Reasoning
Position = 0
starts at the first character of each string.
while String1[Position] == String2[Position]:
checks whether the current characters are the same. If they are, the alphabetical order has not yet been decided.
Position += 1
moves to the next character in both strings.
Once the loop ends, the characters at Position are different.
if ord(String1[Position]) < ord(String2[Position]):
compares the code values of those two characters. For lower-case letters, an earlier letter has a smaller code.
- If the character from
String1is smaller, thenString1comes first, so return1. - Otherwise
String2comes first, so return2.
Example:
Compare silver and slate:
s=s, so continueicompared withlicomes beforel, so the function returns1
Example:
Compare brown and bronze:
b=br=ro=owcompared withnncomes first, so the second string comes first and the function returns2
Key Takeaways
- Lexicographic comparison works by checking the first position where two strings differ.
- A loop is needed to skip over equal leading characters.
- Returning coded results such as
1and2is common when helper functions are used inside sorting routines.
Common Mistakes
- Comparing the whole strings directly, which the question forbids.
- Returning
True/Falseinstead of the required1or2. - Forgetting to move to the next character inside the loop, causing an infinite loop.
- Using
<=or>=instead of finding the first different character.
Things to Be Careful About
- The assumption about a difference before the end of a string means you do not need extra bounds checks here.
- Use character-by-character comparison, not a built-in alphabetical comparison of full strings.
- The return values are not arbitrary:
1means first parameter first,2means second parameter first.
The function Bubble() takes an array of strings as a parameter and sorts the data into ascending alphabetical order, using a bubble sort. The bubble sort uses CompareStrings() to compare each string.
The function returns the sorted list.
Write program code for Bubble().
Save your program.
Copy and paste the program code into part 1(d)(i) in the evidence document.
Answer
def Bubble(DataArray):
for Pass in range(0, len(DataArray) - 1):
for Index in range(0, len(DataArray) - Pass - 1):
if CompareStrings(DataArray[Index], DataArray[Index + 1]) == 2:
Temp = DataArray[Index]
DataArray[Index] = DataArray[Index + 1]
DataArray[Index + 1] = Temp
return DataArray
See program code
Background Concept
Bubble sort repeatedly compares adjacent items and swaps them if they are in the wrong order. After one full pass, the largest remaining item has moved to the end. After each pass, one more item is guaranteed to be in its correct final position.
In this question, the comparison is not done directly with built-in string ordering. Instead, the sort must call CompareStrings() to decide whether two adjacent strings are in the right order.
Understanding the Question
Bubble() receives an array of strings and must sort it into ascending alphabetical order using bubble sort. The key clue is that the sort must use CompareStrings().
That means when two adjacent strings are checked:
- if
CompareStrings(first, second)returns1, they are already in the correct order - if it returns
2, the second should come first, so the two items must be swapped
Approach
Use the standard nested-loop bubble sort structure:
- Outer loop for the passes.
- Inner loop for adjacent comparisons.
- Call
CompareStrings()on elementIndexandIndex + 1. - If the result is
2, swap the two values. - After all passes, return the array.
The inner loop becomes shorter each pass because the largest item in the unsorted section has bubbled to the end.
Step-by-Step Reasoning
for Pass in range(0, len(DataArray) - 1):
runs enough passes to guarantee sorting. For 45 items, this gives 44 passes.
for Index in range(0, len(DataArray) - Pass - 1):
compares adjacent pairs from the start up to the last unsorted position.
if CompareStrings(DataArray[Index], DataArray[Index + 1]) == 2:
asks whether the second item should come before the first. If yes, they are in the wrong order for ascending sort.
The swap is then:
- store the first item temporarily
- move the second item into the first position
- put the temporary value into the second position
That is exactly what:
Temp = DataArray[Index]DataArray[Index] = DataArray[Index + 1]DataArray[Index + 1] = Temp
does.
return DataArray
returns the sorted list so the main program can use it.
Example with a tiny section:
If two adjacent items are silver and bronze, then CompareStrings("silver", "bronze") returns 2 because bronze should come first. They are swapped. Repeating this across many passes gradually moves later letters to the right places.
Key Takeaways
- Bubble sort works by comparing adjacent pairs repeatedly.
- Swapping happens only when two neighbouring items are in the wrong order.
- A helper comparison function can be plugged into a standard sorting algorithm.
Common Mistakes
- Swapping when
CompareStrings()returns1, which would reverse the intended ordering. - Using the wrong inner loop limit and going out of range on
Index + 1. - Forgetting to return the sorted array.
- Comparing items directly instead of using the required
CompareStrings()function.
Things to Be Careful About
- The array is sorted into ascending alphabetical order, not descending.
Index + 1must stay within bounds, so the inner loop limit matters.- Sorting in place is acceptable here because the sorted array is then returned and used immediately.
Write program code to amend the main program to:
- call
Bubble()with the unsorted array as a parameter - call
FormatArray()with the sorted array and output the returned string.
Save your program.
Copy and paste the program code into part 1(d)(ii) in the evidence document.
Answer
DataArray = ReadData()
SortedArray = Bubble(DataArray)
OutputString = FormatArray(SortedArray)
print(OutputString)
See program code
Background Concept
Programs are often built in stages: input, processing, and output. Here:
ReadData()performs the input stageBubble()performs the processing stageFormatArray()prepares the result for displayprint()performs the final output stage
This is a good example of modular design, where each function has a single responsibility.
Understanding the Question
You are amending the main program from part (b). The updated main program must:
- call
Bubble()using the unsorted array - call
FormatArray()using the sorted array - output the returned string
So the key change is inserting the sorting stage between reading the file and formatting the output.
Approach
The correct order is:
- Read the unsorted data.
- Store it in
DataArray. - Send it to
Bubble()and store the returned sorted array. - Send the sorted array to
FormatArray(). - Print the resulting string.
Step-by-Step Reasoning
DataArray = ReadData()
reads the file and stores the unsorted values.
SortedArray = Bubble(DataArray)
calls the bubble sort function and stores the sorted result.
OutputString = FormatArray(SortedArray)
turns the sorted array into one space-separated string.
print(OutputString)
displays the sorted values on screen.
This sequence shows the complete flow from raw file input to sorted text output.
Key Takeaways
- Sorting is a processing step inserted between input and output.
- The main program should pass data from one function to the next in the correct order.
- Naming intermediate variables clearly helps show each stage of the solution.
Common Mistakes
- Passing the unsorted array to
FormatArray()instead of the sorted one. - Calling
Bubble()before the data has been read. - Printing the array directly instead of the formatted string.
Things to Be Careful About
- The function names must be used exactly as defined earlier.
- The sorted array should be the one given to
FormatArray(). - If your
Bubble()function sorts in place, storing the returned value is still a clear and safe approach.
Test your program.
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 1(d)(iii) in the evidence document.
Answer
Input: none
Expected output:
amber aqua azure beige black bronze brown copper cream cyan fawn fuschia gold green grey indigo ivory jade lavender magenta magnolia mango maroon mauve mint mulberry navy olive orange peach periwinkle pink plum purple red rose russet sage scarlet silver slate turquoise violet white yellow
amber aqua azure beige black bronze brown copper cream cyan fawn fuschia gold green grey indigo ivory jade lavender magenta magnolia mango maroon mauve mint mulberry navy olive orange peach periwinkle pink plum purple red rose russet sage scarlet silver slate turquoise violet white yellow
Background Concept
Testing a sorting routine means checking that the output order matches the algorithm's intended order. For ascending alphabetical order, every item should come before the next according to lexicographic comparison.
Because the program uses a custom CompareStrings() function, the expected result should match character-by-character alphabetical ordering, not a different ordering rule.
Understanding the Question
After adding Bubble() to the main program, you must run the program and capture the output. The expected output is the 45 colour names from Data.txt, but now arranged in ascending alphabetical order and displayed as one space-separated string.
Approach
To derive the expected output:
- Start from the original file contents.
- Apply alphabetical ordering.
- Check a few tricky pairs carefully, such as words sharing the same first letters.
- Write the final sorted line exactly as the program should print it.
Step-by-Step Reasoning
The original file is unsorted. After Bubble() repeatedly compares adjacent strings and swaps them when needed, the list becomes alphabetically ordered.
A few important comparisons:
bronzecomes beforebrownbecausebromatches, thenncomes beforew.greencomes beforegreybecausegrematches, thenecomes beforey.magentacomes beforemagnoliabecausemagmatches, thenecomes beforen.plumcomes beforepurplebecausepmatches, thenlcomes beforeu.scarletcomes beforesilver, andsilvercomes beforeslate.
So the fully sorted output is:
amber aqua azure beige black bronze brown copper cream cyan fawn fuschia gold green grey indigo ivory jade lavender magenta magnolia mango maroon mauve mint mulberry navy olive orange peach periwinkle pink plum purple red rose russet sage scarlet silver slate turquoise violet white yellow
That is the line the screenshot should show.
Key Takeaways
- A sorted-output test is really a check that the comparison logic and swap logic both work.
- Lexicographic order depends on the first position where two words differ.
- Testing by predicting the exact output is a valuable debugging skill.
Common Mistakes
- Leaving the output in the original file order instead of sorted order.
- Misordering words with common prefixes, such as
magentaandmagnolia. - Forgetting that the output should still be a single space-separated string, not a Python list display.
Things to Be Careful About
- The program has no keyboard input here; the result comes entirely from
Data.txt. - Every item from the file must appear exactly once in the sorted output.
- Spellings must match the source data exactly, including
fuschiaas given in the file.
A computer program is designed to simulate horses doing show jumping. In show jumping, horses jump over obstacles called fences. A horse successfully jumps a fence if it does not knock the fence down.
The program is written using Object-Oriented Programming (OOP).
The class Horse stores data about the horses.
| Horse | |
|---|---|
Name : STRING | stores the name given to the horse |
MaxFenceHeight : INTEGER | stores the maximum height in cm that the horse can jump, for example 132 |
PercentageSuccess : INTEGER | stores the percentage chance of a horse not knocking down a fence, for example 70 represents a 70% chance of jumping a fence successfully |
Constructor() | initialises Name, MaxFenceHeight and PercentageSuccess to its parameter values |
GetName() | returns the name of the horse |
GetMaxFenceHeight() | returns the maximum height the horse can jump |
Success() | calculates and returns the percentage chance of a horse successfully jumping a specific fence |
Write program code to declare the class Horse and its constructor.
Do not declare the other methods.
Use your programming language's appropriate constructor.
All attributes must be private. If you are writing in Python, include attribute declarations using comments.
Save your program as Question2_N24.
Copy and paste the program code into part 2(a)(i) in the evidence document.
Answer
class Horse:
# __Name : str
# __MaxFenceHeight : int
# __PercentageSuccess : int
def __init__(self, Name, MaxFenceHeight, PercentageSuccess):
self.__Name = Name
self.__MaxFenceHeight = MaxFenceHeight
self.__PercentageSuccess = PercentageSuccess
See program code
Background Concept
In OOP, a class is a template used to create objects. It defines the data each object stores, called attributes, and the operations that can be performed on that data, called methods. A constructor is the special method that runs when a new object is created and is used to initialise the object's attributes. In Python, the constructor is __init__.
Private attributes are used to support encapsulation. That means the internal data of the object should be accessed through the class's own methods rather than directly from outside the class. In Python, using a double underscore such as __Name is the normal way to indicate a private attribute, and this is what Cambridge expects in this style of question.
Understanding the Question
This part asks only for the Horse class declaration and its constructor. The class must store three pieces of data: the horse's name, the maximum fence height it can jump, and its percentage success rate. The question explicitly says not to declare the other methods yet, so only the constructor should be included.
Because this is Python, the question also asks for attribute declarations using comments. That means the attributes should be listed as comments near the top of the class as evidence of their existence and types.
Approach
Define the class Horse, add comment lines showing the three private attributes, then write the Python constructor __init__. The constructor must accept three parameter values and copy them into the private attributes using self.
The key idea is that each horse object will later be created with different values, so the constructor must not hard-code anything.
Step-by-Step Reasoning
class Horse: starts the class definition.
The three comment lines are included because the question asks Python candidates to show attribute declarations using comments:
__Name : str__MaxFenceHeight : int__PercentageSuccess : int
def __init__(self, Name, MaxFenceHeight, PercentageSuccess): is the constructor. In Python, self refers to the object being created. The other three items are the incoming parameter values.
Each assignment stores one parameter into one private attribute:
self.__Name = Nameself.__MaxFenceHeight = MaxFenceHeightself.__PercentageSuccess = PercentageSuccess
That means when a horse is created, for example with Horse('Beauty', 150, 72), those values are stored inside that specific object.
Key Takeaways
You should be able to declare a Python class, write a constructor using __init__, and initialise private attributes from constructor parameters.
Common Mistakes
A common mistake is making the attributes public, for example using self.Name instead of self.__Name. That does not satisfy the requirement for private attributes.
Another mistake is forgetting one of the three attributes or not matching the constructor parameters to the required data items.
Some candidates also add the other methods even though the question says not to declare them in this part.
Things to Be Careful About
Use the exact constructor name __init__ in Python. Use self on every attribute assignment. Keep the attribute names consistent with the rest of the program, because later methods must refer to the same private names. Also include the comment declarations, because the question specifically asks Python candidates to do that.
The get methods GetName() and GetMaxFenceHeight() each return the relevant attribute.
Write program code for the get methods.
Save your program.
Copy and paste the program code into part 2(a)(ii) in the evidence document.
Answer
def GetName(self):
return self.__Name
def GetMaxFenceHeight(self):
return self.__MaxFenceHeight
See program code
Background Concept
Getter methods are simple methods used to return the value of private attributes. They are part of encapsulation, where data is kept private inside the object and accessed in a controlled way through methods.
A getter does not print a value and does not change the object. Its job is only to return the requested attribute so that other parts of the program can use it.
Understanding the Question
This part says that GetName() and GetMaxFenceHeight() each return the relevant attribute. So the task is to add two methods to the Horse class:
- one that returns the name
- one that returns the maximum fence height
Nothing else is required here. The methods should simply return the correct private field.
Approach
For each getter, write a method with self as the only parameter. Inside the method, use return followed by the matching private attribute.
The important point is that the methods must return the value, not display it. Later code may want to store it, compare it, or place it inside a larger output message.
Step-by-Step Reasoning
def GetName(self): defines a method called GetName.
return self.__Name sends back the horse's name stored in the private attribute.
def GetMaxFenceHeight(self): defines the second getter.
return self.__MaxFenceHeight sends back the maximum height that horse can jump.
These methods allow code outside the class to access the values safely while keeping the attributes private.
Key Takeaways
You should recognise that a getter method is just a controlled way to return a private attribute. In Python, that means a short method using return self.__AttributeName.
Common Mistakes
A very common mistake is using print(...) instead of return. Printing only displays the value and does not send it back to the calling code.
Another mistake is returning the wrong attribute, such as returning the height from GetName().
Some candidates also forget self, which makes the method definition invalid inside the class.
Things to Be Careful About
The method names must match the question exactly: GetName() and GetMaxFenceHeight(). The returned attribute names must also match the private attribute names created in the constructor. Keep the indentation correct so these methods are inside the class.
The array Horses stores objects of type Horse.
The program has two horses:
- The horse named 'Beauty' can jump a maximum height of 150 cm and has a success percentage rate of 72%.
- The horse named 'Jet' can jump a maximum height of 160 cm and has a success percentage rate of 65%.
Write program code to:
- declare the array,
Horses, local to the main program with space for twoHorseobjects - store the two horses described in the array
- output the name of both
Horseobjects from the array.
Save your program.
Copy and paste the program code into part 2(b)(i) in the evidence document.
Answer
def main():
Horses = [None] * 2
Horses[0] = Horse('Beauty', 150, 72)
Horses[1] = Horse('Jet', 160, 65)
print(Horses[0].GetName())
print(Horses[1].GetName())
if __name__ == '__main__':
main()
See program code
Background Concept
An array, or list in Python, can store references to objects as well as simple data such as numbers or strings. When you create an object with a constructor such as Horse(...), the list stores that object reference in one position.
Once an object is stored in a list, you can access it by index and call its methods using dot notation, for example Horses[0].GetName().
Understanding the Question
This part asks for three things in the main program:
- declare an array called
Horseswith space for twoHorseobjects - create the two given horses and store them in the array
- output the name of both horses from the array
The values are fixed by the question:
Beauty, 150, 72Jet, 160, 65
Approach
Use a Python list of length 2, initially filled with None. Then create each Horse object with the constructor and store it into one list position. Finally, call GetName() on each stored object and print the returned name.
To make the array local to the main program, place it inside a main() function.
Step-by-Step Reasoning
def main(): starts the main program section.
Horses = [None] * 2 creates a list with two positions. At this stage, the positions do not yet contain Horse objects.
Horses[0] = Horse('Beauty', 150, 72) creates the first horse object and stores it in the first position.
Horses[1] = Horse('Jet', 160, 65) creates the second horse object and stores it in the second position.
print(Horses[0].GetName()) accesses the first object and prints its name.
print(Horses[1].GetName()) does the same for the second object.
The final two lines run main() when the program file is executed.
Key Takeaways
You should be able to create an array of objects, fill it with constructed instances, and access object methods through array elements.
Common Mistakes
One mistake is storing only the horse names as strings instead of actual Horse objects. That would not meet the requirement to store objects of type Horse.
Another mistake is forgetting that Python lists are zero-indexed and trying to use positions 1 and 2 instead of 0 and 1.
Candidates sometimes also print literal text such as Beauty and Jet directly, rather than outputting the names from the array as the question requests.
Things to Be Careful About
Keep Horses inside main() so it is local to the main program. Use the constructor parameters in the correct order: name, maximum fence height, percentage success. Make sure you call GetName() with brackets.
Test your program.
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 2(b)(ii) in the evidence document.
Answer
Using the horses created in part (b)(i), the output is:
Beauty
Jet
Beauty\nJet
Background Concept
Testing a program means running it with known data and checking that the actual output matches the expected output. For a very small task like this one, the expected output can be predicted directly from the code.
Here, each print(...) statement writes one line to the console.
Understanding the Question
This part does not ask for new code. It asks you to test the program from part (b)(i) and capture the output. Since the program prints the names of the two stored horse objects, the result should be the two names on separate lines.
Approach
Look at the order of the print statements in part (b)(i):
- first print the name from
Horses[0] - then print the name from
Horses[1]
Since those positions contain Beauty and Jet, that determines the exact output.
Step-by-Step Reasoning
Horses[0] stores the Horse object created with the name Beauty.
Calling GetName() on that object returns Beauty, so the first line printed is Beauty.
Horses[1] stores the Horse object created with the name Jet.
Calling GetName() on that object returns Jet, so the second line printed is Jet.
Because print moves to a new line after each output, the two names appear on separate lines.
Key Takeaways
You should be able to read short code and predict the exact console output it produces.
Common Mistakes
A common mistake is reversing the order of the output lines. The output must match the order of the print statements.
Another mistake is adding extra text that the program does not actually print.
Things to Be Careful About
When giving expected output, include only what appears on the console. Do not add explanation text inside the output block itself unless the program prints it.
The class Fence stores data about the fences. Each fence has a height in cm and a risk number.
The risk is a whole number between 1 and 5 inclusive. A risk of 1 means the fence is the easiest type to jump. A risk of 5 means the fence is the hardest type to jump.
| Fence | |
|---|---|
Height : INTEGER | stores the height of the fence in cm the height is between 70 and 180 inclusive |
Risk : INTEGER | stores the risk as a whole number between 1 and 5 inclusive |
Constructor() | initialises Height and Risk to its parameter values |
GetHeight() | returns the height of the fence |
GetRisk() | returns the risk of the fence |
Write program code to declare the class Fence, its constructor and get methods.
Use your programming language's appropriate constructor.
All attributes must be private.
If you are writing in Python, include attribute declarations using comments.
Save your program.
Copy and paste the program code into part 2(c)(i) in the evidence document.
Answer
class Fence:
# __Height : int
# __Risk : int
def __init__(self, Height, Risk):
self.__Height = Height
self.__Risk = Risk
def GetHeight(self):
return self.__Height
def GetRisk(self):
return self.__Risk
See program code
Background Concept
A class can be used to model real-world items by storing related data together. In this case, each Fence object stores two values: its height and its risk. As with the Horse class, encapsulation means the attributes are private and are accessed through getter methods.
The constructor sets up a newly created fence so that it immediately contains valid data values.
Understanding the Question
This part asks for the Fence class, including:
- private attributes for height and risk
- a constructor that initialises both values
- getter methods
GetHeight()andGetRisk()
Because the question is written in the same OOP style as the Horse class, the new class should follow the same pattern.
Approach
Define class Fence, add comment declarations for the two private attributes, then write __init__, GetHeight, and GetRisk. Each getter should return exactly one stored attribute.
Step-by-Step Reasoning
class Fence: begins the class definition.
The comment lines show the existence and intended types of the private attributes:
__Height : int__Risk : int
def __init__(self, Height, Risk): is the constructor. It accepts the two incoming values when a Fence object is created.
self.__Height = Height stores the height.
self.__Risk = Risk stores the risk.
def GetHeight(self): defines the getter for height, and return self.__Height sends that value back.
def GetRisk(self): defines the getter for risk, and return self.__Risk sends that value back.
Key Takeaways
You should be able to apply the same class pattern to a different object type: private data, constructor, and simple getter methods.
Common Mistakes
A common mistake is forgetting to make the attributes private by omitting the double underscores.
Another mistake is mixing up the getter returns, for example returning risk from GetHeight().
Some candidates also forget the comment declarations even though the question specifically asks Python candidates to include them.
Things to Be Careful About
Use the exact class and method names from the question. Keep the parameter order as Height, Risk, and keep the indentation correct so all methods belong to the class.
The array Course stores four Fence objects. The user inputs the height and risk for each fence, and these are validated before each fence is created.
Amend the main program to:
- declare the local array
Course - take as input the data for four fences from the user
- loop the input until both the height and risk are valid for each fence
- create an instance of
Fencefor each of the four valid fences and store each instance in the array.
Save your program.
Copy and paste the program code into part 2(c)(ii) in the evidence document.
Answer
Course = [None] * 4
for Count in range(4):
while True:
Height = int(input())
Risk = int(input())
if 70 <= Height <= 180 and 1 <= Risk <= 5:
Course[Count] = Fence(Height, Risk)
break
See program code
Background Concept
Validation checks whether input data is acceptable before the program uses it. Here, the rules are given clearly:
- height must be between 70 and 180 inclusive
- risk must be between 1 and 5 inclusive
When input must be repeated until it becomes valid, a loop is needed. In Python, while True with a break after successful validation is a common way to do this.
Understanding the Question
This part asks you to amend the main program so that it can build an array called Course containing four Fence objects. For each of the four fences, the user enters a height and a risk. If either value is invalid, the program must ask again for that fence until both values are valid.
Only after both values are valid should the program create the Fence object and store it in the array.
Approach
First create a list with space for four Fence objects. Then use a for loop to process fence 1, fence 2, fence 3, and fence 4. Inside that loop, use a validation loop that keeps taking input until both tests are true.
The logical test must use and because both conditions must be satisfied at the same time.
Step-by-Step Reasoning
Course = [None] * 4 creates a list with four positions ready to store Fence objects.
for Count in range(4): repeats the process four times, once for each fence.
while True: starts a loop that will continue until valid data is entered.
Height = int(input()) reads the height.
Risk = int(input()) reads the risk.
if 70 <= Height <= 180 and 1 <= Risk <= 5: performs both validation checks:
- the height is within the inclusive allowed range
- the risk is within the inclusive allowed range
If both are true, Course[Count] = Fence(Height, Risk) creates the object and stores it in the correct array position.
break exits the validation loop so the program moves on to the next fence.
If either value is invalid, the if body is skipped, no object is created, and the loop repeats for the same fence.
Key Takeaways
You should be able to combine fixed-count input, validation, and object creation. A very common pattern is: input values, test them, and only then construct and store the object.
Common Mistakes
A common mistake is using or instead of and in the validation condition. That would allow partly invalid data through.
Another mistake is creating the Fence object before the validation check. The question says the values must be validated before each fence is created.
Candidates also sometimes use the wrong bounds, such as excluding 70 or 180 even though the question says inclusive.
Things to Be Careful About
Python lists are zero-indexed, so range(4) correctly gives positions 0 to 3. Make sure the break is inside the if so it only happens after valid input. The question asks for range validation, not exception handling for non-integer input, so the essential requirement is the bounds test.
The chance of a horse jumping a fence without knocking it down is calculated as follows.
If the height of the fence is more than the maximum height a horse can jump, the success percentage is 20% of the horse's PercentageSuccess. The risk does not affect this value.
If the height of the fence is less than or equal to the maximum height a horse can jump, the risk gives a modifier value to multiply with the horse's PercentageSuccess.
The risk values and their modifiers are given in this table:
| Risk | Modifier |
|---|---|
| 5 | 0.6 |
| 4 | 0.7 |
| 3 | 0.8 |
| 2 | 0.9 |
| 1 | 1.0 |
For example:
- The horse Jet has
PercentageSuccessof 65 andMaxFenceHeightof 160. - A fence has a height of 140 and a risk of 3.
- The height of the fence is less than the horse's
MaxFenceHeight, therefore the risk is used. - The risk of 3 gives the modifier 0.8.
- The modifier 0.8 is multiplied by the horse's
PercentageSuccessof 65, which gives 52. - The chance of the horse successfully jumping this fence is 52%.
The method Success() in the Horse class:
- takes the height and risk of a fence as parameters
- calculates the percentage chance of success for that horse jumping the fence without knocking it down
- returns the calculated percentage chance of success as a real number.
Write program code for Success().
Save your program.
Copy and paste the program code into part 2(d) in the evidence document.
Answer
def Success(self, Height, Risk):
if Height > self.__MaxFenceHeight:
return self.__PercentageSuccess * 0.2
if Risk == 5:
Modifier = 0.6
elif Risk == 4:
Modifier = 0.7
elif Risk == 3:
Modifier = 0.8
elif Risk == 2:
Modifier = 0.9
else:
Modifier = 1.0
return self.__PercentageSuccess * Modifier
See program code
Background Concept
A method in a class can use the object's private attributes together with parameters passed in from elsewhere. This is useful when the result depends partly on the object itself and partly on values supplied at the time the method is called.
This question also tests selection. Selection means choosing one path or another depending on a condition. Here there are two stages of decision-making:
- first decide whether the fence is higher than the horse can jump
- if not, choose the correct modifier from the risk table
Understanding the Question
The Success() method belongs to a Horse object. It receives a fence height and a fence risk as parameters, then calculates that horse's chance of jumping the fence successfully.
The rules are:
- if the fence is too high, ignore risk and use 20% of the horse's
PercentageSuccess - otherwise, use the risk to choose a modifier and multiply that by
PercentageSuccess - return the answer as a real number
Approach
Start with the height test, because the question says that when the fence is too high, the risk does not matter. That means the method can return immediately in that case.
If the fence height is not too high, use an if / elif chain to map the risk value to its modifier. Then multiply the modifier by the horse's success percentage and return the result.
Step-by-Step Reasoning
def Success(self, Height, Risk): defines a method that takes two pieces of fence data.
if Height > self.__MaxFenceHeight: checks whether the fence is higher than the horse's maximum jump height.
If that is true, return self.__PercentageSuccess * 0.2 gives 20% of the horse's normal success percentage and ends the method immediately.
If the fence is not too high, the code reaches the risk tests:
- risk 5 gives
0.6 - risk 4 gives
0.7 - risk 3 gives
0.8 - risk 2 gives
0.9 - otherwise the only remaining valid value is risk 1, so the modifier is
1.0
Finally, return self.__PercentageSuccess * Modifier calculates and returns the success chance.
For example, if a horse has PercentageSuccess = 65 and the risk modifier is 0.8, the method returns 52.0.
Key Takeaways
You should be able to translate written rules into conditional code and spot when one condition has priority over another.
Common Mistakes
A very common mistake is applying the risk modifier even when the fence is higher than the horse's maximum height. The question says risk does not affect that case.
Another mistake is reversing the comparison and checking Height < self.__MaxFenceHeight for the wrong branch.
Some candidates also return whole numbers only, even though the question says the method should return a real number.
Things to Be Careful About
Check the condition carefully: the special 20% case happens only when the fence height is more than the maximum height. If the height is equal to the maximum, the risk-based calculation must be used. Also keep the modifier values exactly as shown in the table.
Write program code to amend the main program to:
- calculate and output the chance of the first horse jumping each of the four fences without knocking each fence down
- calculate and output the chance of the second horse jumping each of the four fences without knocking each fence down.
All outputs must have appropriate messages including the name of the horse and the fence number.
An example output for one horse jumping two fences is:
"The horse Fox at fence 1 has a 68% chance of success
The horse Fox at fence 2 has a 72% chance of success"
Save your program.
Copy and paste the program code into part 2(e)(i) in the evidence document.
Answer
for HorseCount in range(2):
for FenceCount in range(4):
Chance = Horses[HorseCount].Success(
Course[FenceCount].GetHeight(),
Course[FenceCount].GetRisk()
)
print(f'The horse {Horses[HorseCount].GetName()} at fence {FenceCount + 1} has a {Chance:g}% chance of success')
See program code
Background Concept
When one set of items must be processed against every item in another set, nested loops are a natural solution. Here there are two horses and four fences, so each horse must be tested against each fence.
This also shows how objects interact: a Horse object uses its Success() method, and the fence data needed by that method is obtained from each Fence object using getters.
Understanding the Question
You must amend the main program so that it outputs the success chance:
- for the first horse at fences 1 to 4
- then for the second horse at fences 1 to 4
The message must include both the horse name and the fence number. That means the code must calculate the chance and produce a clear sentence for every horse-fence combination.
Approach
Use an outer loop for the two horses and an inner loop for the four fences. For each pair, call:
GetHeight()on the relevant fenceGetRisk()on the relevant fenceSuccess(...)on the relevant horse
Store the returned value in a variable such as Chance, then print it in the required message.
Step-by-Step Reasoning
for HorseCount in range(2): loops through the two horses, positions 0 and 1.
Inside that, for FenceCount in range(4): loops through the four fences, positions 0 to 3.
Chance = Horses[HorseCount].Success(...) calls the current horse's Success() method.
The two parameters passed in are taken from the current fence object:
Course[FenceCount].GetHeight()Course[FenceCount].GetRisk()
So the method has everything it needs: the horse contributes its own private data, and the fence contributes height and risk.
The print line then creates the required message. FenceCount + 1 is used because list positions start at 0 but fence numbers for the user should start at 1.
The :g format removes unnecessary trailing .0 from whole-number results while still allowing decimal values when needed.
Key Takeaways
You should be able to combine nested loops, object arrays, method calls, and formatted output in one section of code.
Common Mistakes
A common mistake is printing the fence array index directly, which would give fence numbers 0 to 3 instead of 1 to 4.
Another mistake is trying to pass the whole fence object into Success() even though the method expects height and risk values as separate parameters.
Candidates sometimes also output the object reference instead of the horse's name by forgetting to call GetName().
Things to Be Careful About
Keep the loop bounds correct: 2 horses and 4 fences. Make sure the horse loop is the outer loop so the output order is first horse then second horse, as requested. Use brackets on every method call.
Write program code to amend the main program to:
- calculate and output the average chance of success for each horse jumping over all four fences without knocking each fence down (the average is the total of values divided by the quantity of values). An example output for one horse jumping all of the fences is:
"The horse Fox has an average 70% chance of jumping over all four fences"
- output the name of the horse that has the highest average chance of success.
You can assume that each average will be different.
All outputs must have appropriate messages.
Save your program.
Copy and paste the program code into part 2(e)(ii) in the evidence document.
Answer
Average = [0.0] * 2
for HorseCount in range(2):
Total = 0
for FenceCount in range(4):
Total += Horses[HorseCount].Success(
Course[FenceCount].GetHeight(),
Course[FenceCount].GetRisk()
)
Average[HorseCount] = Total / 4
print(f'The horse {Horses[HorseCount].GetName()} has an average {Average[HorseCount]:g}% chance of jumping over all four fences')
if Average[0] > Average[1]:
print(f'The horse {Horses[0].GetName()} has the highest average chance of success')
else:
print(f'The horse {Horses[1].GetName()} has the highest average chance of success')
See program code
Background Concept
An average is found by adding all values in a set and dividing by how many values there are. In programming, this usually means using a running total inside a loop, then performing the division after the loop finishes.
To find the largest result, you compare the calculated values. Since the question says you may assume the averages are different, a simple if / else comparison is enough.
Understanding the Question
You must extend the main program so that it:
- calculates the average chance of success for each horse across all four fences
- outputs a message for each average
- outputs the name of the horse with the highest average chance
Because both horses jump the same number of fences, each average is the total divided by 4.
Approach
Use a loop over the two horses. For each horse, reset Total to 0, then loop over the four fences and add each calculated success chance. After the inner loop finishes, divide by 4 to get the average and print it.
Store the average for each horse in a list so both values are available for the final comparison.
Step-by-Step Reasoning
Average = [0.0] * 2 creates space to store the two average values.
for HorseCount in range(2): processes each horse.
Total = 0 resets the running total for that horse. This must happen at the start of each horse's calculation.
The inner loop repeats across the four fences. On each pass, Success(...) returns the horse's chance for that fence, and that value is added to Total.
After all four fences have been processed, Average[HorseCount] = Total / 4 calculates the average for that horse.
The print statement outputs the horse's name and the average in a full sentence.
Finally, the if statement compares Average[0] and Average[1]. If the first is larger, the first horse's name is printed; otherwise the second horse's name is printed.
Key Takeaways
You should be comfortable with the pattern: initialise total, accumulate in a loop, divide after the loop, then compare final results.
Common Mistakes
One frequent mistake is forgetting to reset Total for each horse. That would cause the second horse's result to include the first horse's values as well.
Another mistake is dividing inside the inner loop instead of after all four values have been added.
Some candidates compare totals rather than averages. Here the result would be the same because both horses have four fences, but the question specifically asks for averages, so you should calculate and print averages.
Things to Be Careful About
Divide by exactly 4, because there are four fences. Use the same fence data and success calculation as in part (e)(i). The final comparison is simpler because the question guarantees the averages will be different.
Test your program with the following input data for four fences:
| Height | Risk |
|---|---|
| 152 | 5 |
| 121 | 1 |
| 130 | 3 |
| 145 | 4 |
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 2(e)(iii) in the evidence document.
Answer
Using the fence inputs 152, 5, 121, 1, 130, 3, 145, 4, the output is:
The horse Beauty at fence 1 has a 14.4% chance of success
The horse Beauty at fence 2 has a 72% chance of success
The horse Beauty at fence 3 has a 57.6% chance of success
The horse Beauty at fence 4 has a 50.4% chance of success
The horse Jet at fence 1 has a 39% chance of success
The horse Jet at fence 2 has a 65% chance of success
The horse Jet at fence 3 has a 52% chance of success
The horse Jet at fence 4 has a 45.5% chance of success
The horse Beauty has an average 48.6% chance of jumping over all four fences
The horse Jet has an average 50.375% chance of jumping over all four fences
The horse Jet has the highest average chance of success
See expected output
Background Concept
A test run checks whether the full program behaves correctly when given specific input values. For a program like this, the expected output can be worked out by following the rules of the Success() method for each horse and each fence.
Once each individual success value is known, the average is found by adding the four values for one horse and dividing by 4.
Understanding the Question
The fence input data is fixed in the question:
- fence 1: height 152, risk 5
- fence 2: height 121, risk 1
- fence 3: height 130, risk 3
- fence 4: height 145, risk 4
The two horses are:
- Beauty: maximum 150, percentage success 72
- Jet: maximum 160, percentage success 65
You need the full output for all fence chances, both averages, and the horse with the highest average.
Approach
Work through Beauty first, then Jet, matching the program's output order. For each fence, first check whether the fence height is above the horse's maximum. If it is, use 20% of the horse's percentage success. If not, use the risk modifier table.
After calculating the four values for one horse, add them and divide by 4 to get the average.
Step-by-Step Reasoning
For Beauty:
- Fence 1 has height 152, which is more than Beauty's maximum of 150, so use
72 × 0.2 = 14.4 - Fence 2 has height 121, which is within the maximum, and risk 1 gives modifier
1.0, so72 × 1.0 = 72 - Fence 3 has risk 3, so modifier
0.8, giving72 × 0.8 = 57.6 - Fence 4 has risk 4, so modifier
0.7, giving72 × 0.7 = 50.4
Beauty's average is:
- total
14.4 + 72 + 57.6 + 50.4 = 194.4 - average
194.4 / 4 = 48.6
For Jet:
- Fence 1 has height 152, which is not more than Jet's maximum of 160, so risk 5 is used:
65 × 0.6 = 39 - Fence 2 has risk 1, so
65 × 1.0 = 65 - Fence 3 has risk 3, so
65 × 0.8 = 52 - Fence 4 has risk 4, so
65 × 0.7 = 45.5
Jet's average is:
- total
39 + 65 + 52 + 45.5 = 201.5 - average
201.5 / 4 = 50.375
Now compare the averages:
- Beauty:
48.6 - Jet:
50.375
Jet has the higher average, so the last output line names Jet.
Key Takeaways
You should be able to trace a program that combines object data, conditional calculations, and averages, and then predict the exact output.
Common Mistakes
A common mistake is using the risk modifier for Beauty at fence 1, even though the fence is higher than Beauty's maximum height. In that case the special 20% rule must be used.
Another mistake is assuming whole-number percentages only. Some results are real values such as 14.4, 57.6, and 50.375.
Candidates also sometimes compare only one fence result instead of the averages when deciding the final horse.
Things to Be Careful About
Keep the output order exactly as the program would print it: all four fences for the first horse, then all four for the second, then the two averages, then the highest average message. Also remember that Jet can use the normal risk rule at fence 1 because 152 is below Jet's maximum of 160.
A linked list stores positive integer data in a 2D array. The first dimension of the array stores the integer data. The second dimension of the array stores the pointer to the next node in the linked list.
A linked list node with no data is initialised with the integer -1. These nodes are linked together as an empty list. A pointer of -1 identifies that node as the last node.
The linked list can store 20 nodes.
The global 2D array LinkedList stores the linked list.
LinkedList is initialised as an empty list. The data in each node is initialised to -1. Each node's pointer stores the index of the next node. The last node stores the pointer value -1, which indicates it is the last node.
The global variable FirstEmpty stores the index of the first element in the empty list. This is the first node in the empty linked list when it is initialised, which is index 0.
The global variable FirstNode stores the index of the first element in the linked list. There is no data in the linked list when it is initialised, so FirstNode is initialised to -1.
This diagram shows the content of the initialised array.
FirstEmpty = 0
FirstNode = -1
| Index | Data | Pointer |
|---|---|---|
| 0 | -1 | 1 |
| 1 | -1 | 2 |
| 2 | -1 | 3 |
| 3 | -1 | 4 |
| 4 | -1 | 5 |
| | |
| 19 | -1 | -1 |
Write program code for the main program to declare and initialise LinkedList, FirstNode and FirstEmpty.
Save your program as Question3_N24.
Copy and paste the program code into part 3(a) in the evidence document.
Answer
LinkedList = [[0 for Column in range(2)] for Row in range(20)]
for Index in range(20):
LinkedList[Index][0] = -1
LinkedList[Index][1] = Index + 1
LinkedList[19][1] = -1
FirstEmpty = 0
FirstNode = -1
See program code
Background Concept
An array-based linked list stores each node in a row of an array instead of using dynamic memory pointers. In this question, each node has two parts:
LinkedList[Index][0]stores the data value.LinkedList[Index][1]stores the pointer to the next node.
There are actually two linked lists being maintained:
- the real data list, starting at
FirstNode - the empty list (also called the free list), starting at
FirstEmpty
A value of -1 is used in two places:
- as the data value for an unused node
- as a null pointer meaning "no next node"
When the structure is first created, there is no real data yet, so the whole array belongs to the empty list.
Understanding the Question
You are asked only to declare and initialise the global structure. That means you must create space for 20 nodes and make it match the diagram and description:
- every data field must be
-1 - the pointers in the empty list must chain through the array in order
- index
19must be the end of that chain, so its pointer is-1 FirstEmptymust point to the first free node, which is index0FirstNodemust be-1because the actual linked list is empty
So this part is about building the starting state correctly.
Approach
The easiest approach in Python is:
- create a 20 by 2 array
- loop through every row
- put
-1in the data column - put the next index in the pointer column
- fix the final pointer so it becomes
-1 - assign the two start pointers
That exactly matches the given table.
Step-by-Step Reasoning
LinkedList = [[0 for Column in range(2)] for Row in range(20)]
- This creates 20 rows.
- Each row has 2 columns.
- At this moment the values are temporary zeroes; they are replaced in the loop.
for Index in range(20):
- This visits row
0up to row19.
LinkedList[Index][0] = -1
- Column
0is the data field. - Setting it to
-1marks the node as unused.
LinkedList[Index][1] = Index + 1
- Column
1is the pointer field. - For row
0, this becomes1; for row1, it becomes2; and so on. - So each empty node points to the next empty node.
After the loop, row 19 would temporarily point to 20, which is outside the array, so it must be corrected:
LinkedList[19][1] = -1
- This makes node
19the end of the empty list.
Then set the two global pointers:
FirstEmpty = 0because the first free node is row0FirstNode = -1because the real linked list currently has no first node
That gives the required initial state.
Key Takeaways
- An array-based linked list uses array indices as pointers.
- A free list keeps track of unused nodes.
- Initialisation matters: if the starting pointers are wrong, every later insert or remove operation will fail.
- In this representation,
-1is both the empty-data marker and the null pointer value.
Common Mistakes
- Forgetting to set the last pointer to
-1, which leaves an invalid pointer such as20. - Setting
FirstNodeto0; that would wrongly claim there is already data in the list. - Initialising only the pointer column and forgetting to set the data column to
-1. - Creating only 20 values instead of a 20 by 2 structure.
Things to Be Careful About
- The array uses indices
0to19, not1to20. - Keep the column meanings consistent:
[0]is data and[1]is pointer. FirstEmptypoints to the free list, not to the data list.- The question asks for globals, so later procedures must work with the same
LinkedList,FirstNodeandFirstEmpty.
The procedure InsertData() takes five positive integers as input from the user and inserts these into the linked list.
Each data item is inserted at the front of the linked list.
The table shows the steps to follow depending on the state of the linked list:
| Linked list state | Steps |
|---|---|
| not full | insert the data in the index pointed to by FirstEmpty change the pointer to the index pointed to by FirstNode change the values of FirstNode and FirstEmpty |
| full | end the procedure |
Any node that is at the end of the linked list has a pointer of -1.
Write program code for InsertData().
Save your program.
Copy and paste the program code into part 3(b) in the evidence document.
Answer
def InsertData():
global LinkedList, FirstEmpty, FirstNode
for Count in range(5):
DataItem = int(input())
if FirstEmpty == -1:
return
NewNode = FirstEmpty
FirstEmpty = LinkedList[NewNode][1]
LinkedList[NewNode][0] = DataItem
LinkedList[NewNode][1] = FirstNode
FirstNode = NewNode
See program code
Background Concept
In an array-based linked list, inserting a new item does not create a brand-new node dynamically. Instead, you take the first available node from the empty list, store the data in it, and link it into the real list.
Because this question says each new value is inserted at the front, the new node becomes the first node of the linked list every time.
The important pointer variables are:
FirstEmpty: first node in the free listFirstNode: first node in the real data list
If FirstEmpty is -1, there are no free nodes left, so the list is full.
Understanding the Question
The procedure must:
- input exactly five positive integers
- for each value, try to insert it at the front of the linked list
- stop immediately if the linked list is full
The table in the question gives the exact logic for the "not full" case:
- insert data into the node pointed to by
FirstEmpty - change that node's pointer to the index pointed to by
FirstNode - change
FirstNodeandFirstEmpty
That means you must manipulate both lists correctly.
Approach
For each of the five inputs:
- check whether
FirstEmptyis-1 - if so, end the procedure
- otherwise remember the free node index in a temporary variable such as
NewNode - move
FirstEmptyon to the next free node - store the data in
NewNode - make
NewNodepoint to the old first node of the linked list - update
FirstNodeso the new node becomes the head
This order is important, because if you overwrite the pointer too early you can lose the rest of the free list.
Step-by-Step Reasoning
for Count in range(5):
- The question says five integers, so a count-controlled loop is suitable.
DataItem = int(input())
- Reads one integer value from the user each time round the loop.
if FirstEmpty == -1:
return
- If
FirstEmptyis-1, there is no free node available. - The question says for the full state: end the procedure.
returndoes exactly that.
NewNode = FirstEmpty
- Save the index of the free node that will be used.
- This is the node where the new data will be stored.
FirstEmpty = LinkedList[NewNode][1]
- Before reusing
NewNode, advanceFirstEmptyto the next free node. - This removes
NewNodefrom the empty list.
LinkedList[NewNode][0] = DataItem
- Store the user's value in the data field of the node.
LinkedList[NewNode][1] = FirstNode
- Make the new node point to what used to be the first node of the real linked list.
- If the list was empty,
FirstNodeis-1, so the new node becomes a single-node list.
FirstNode = NewNode
- The new node is now the first node of the real linked list.
After each insertion, the head moves to the newly inserted item. That is why the list ends up in reverse order of input.
Key Takeaways
- A free-list node must be removed from the empty list before it is reused.
- Head insertion is efficient because only a few pointers change.
- Pointer update order matters in linked-list operations.
- Repeated front insertion reverses the order of the inputs.
Common Mistakes
- Changing
LinkedList[NewNode][1]before movingFirstEmpty, which loses the rest of the free list. - Setting
FirstNodetoo early, so the new node points to itself instead of the old first node. - Forgetting the full-list test
FirstEmpty == -1. - Inserting at the end instead of the front, which does not match the question.
Things to Be Careful About
- Use the same global names exactly:
LinkedList,FirstEmpty,FirstNode. - In Python, modifying globals inside a function requires the
globaldeclaration. - The data field is
[0]; the pointer field is[1]. - The input order and output order will differ because every insertion happens at the front.
The procedure OutputLinkedList() outputs the data in the linked list in order by following the pointers from FirstNode.
Write program code for OutputLinkedList().
Save your program.
Copy and paste the program code into part 3(c)(i) in the evidence document.
Answer
def OutputLinkedList():
global LinkedList, FirstNode
CurrentNode = FirstNode
while CurrentNode != -1:
print(LinkedList[CurrentNode][0])
CurrentNode = LinkedList[CurrentNode][1]
See program code
Background Concept
To output the contents of a linked list, you do not loop through the array from index 0 to 19. That would include unused nodes and would ignore the logical order of the list. Instead, you begin at FirstNode and follow the stored pointers.
This is called traversal.
The traversal rule is:
- start at the head pointer
- process the current node
- move to the next node using its pointer
- stop when the pointer becomes
-1
Understanding the Question
The procedure must output the data in linked-list order. The question explicitly says this order is found by following the pointers from FirstNode.
So the task is not about the physical row order in the array. It is about the logical order defined by the links.
Approach
Use one variable, for example CurrentNode, to move through the list.
- set
CurrentNodetoFirstNode - while
CurrentNodeis not-1 - output the data at that node
- move
CurrentNodeto the next pointer
That is the standard linked-list traversal pattern.
Step-by-Step Reasoning
CurrentNode = FirstNode
- This begins at the first actual node in the data list.
- If the list is empty,
FirstNodeis-1and the loop will not run.
while CurrentNode != -1:
-1means there is no node to visit.- So the loop continues as long as a valid node index exists.
print(LinkedList[CurrentNode][0])
- Output the data field of the current node.
CurrentNode = LinkedList[CurrentNode][1]
- Move along the list by following the stored pointer.
- This is the key step that preserves linked-list order.
Suppose the list is 8 -> 3 -> 2 -> 1 -> 5 -> -1.
The traversal visits nodes in exactly that sequence and prints 8, then 3, then 2, then 1, then 5.
Key Takeaways
- Linked lists are traversed by pointers, not by array position order.
FirstNodeis the entry point to the list.-1is the stopping condition.- One pointer variable is enough to walk through the list.
Common Mistakes
- Using
for Index in range(20)instead of following the pointers. - Printing pointer values instead of data values.
- Forgetting to move to the next node, which causes an infinite loop.
- Stopping when the node data is
-1instead of when the pointer/index is-1.
Things to Be Careful About
- If the list is empty, this procedure should simply print nothing.
- Keep
[0]for data and[1]for pointer consistent. - Use
CurrentNode != -1as the loop condition, notCurrentNode < 20. - The logical list order may be very different from the order of row numbers in the array.
Amend the main program to call InsertData() and then OutputLinkedList().
Save your program.
Copy and paste the program code into part 3(c)(ii) in the evidence document.
Answer
InsertData()
OutputLinkedList()
See program code
Background Concept
A main program controls the overall flow of execution by calling procedures in the required order. In procedural programming, each procedure performs one job, and the main program combines them to complete the task.
Here:
InsertData()adds the five values into the linked listOutputLinkedList()displays the linked list contents
Understanding the Question
This part says to amend the main program so that it first inserts the data and then outputs the linked list.
That means the main program must call the procedures in this order:
InsertData()OutputLinkedList()
If the order were reversed, the output would happen before the user had inserted any values.
Approach
Simply add the two procedure calls to the main program after all declarations and procedure definitions are in place.
The only real thinking point is the order: insertion must happen before output.
Step-by-Step Reasoning
InsertData()
- This collects five input values from the user.
- Each is inserted at the front of the linked list.
OutputLinkedList()
- Once the list has been built, this traverses it from
FirstNodeand prints the stored data values.
Because the second procedure depends on the results of the first, this ordering is essential.
Key Takeaways
- The main program coordinates separate procedures.
- Procedure calls must be placed in a logical order.
- Output usually comes after processing, not before it.
Common Mistakes
- Calling
OutputLinkedList()beforeInsertData(). - Forgetting to include one of the calls.
- Putting the calls inside the wrong procedure instead of in the main program.
Things to Be Careful About
- This part asks for an amendment, so only the extra main-program lines are needed.
- Make sure the procedures have already been defined before they are called.
- Keep the names exactly as given:
InsertData()andOutputLinkedList().
Test your program with the test data:
5 1 2 3 8
Take a screenshot of the output.
Save your program.
Copy and paste the screenshot into part 3(c)(iii) in the evidence document.
Answer
For input 5 1 2 3 8:
8
3
2
1
5
See expected output
Background Concept
When data is always inserted at the front of a linked list, the newest item becomes the head node. This means the final linked-list order is the reverse of the input order.
After that, an output procedure prints values by following the list from the current head to the end.
Understanding the Question
You are not being asked to write more code here. You are being asked to run or mentally trace the program using the test data 5 1 2 3 8 and determine what appears on screen.
The crucial clue is: each value is inserted at the front.
Approach
Take the input values one at a time and update the logical order of the list after each insertion.
Start with an empty list.
- insert
5 - insert
1at the front - insert
2at the front - insert
3at the front - insert
8at the front
Then read the finished list from front to back.
Step-by-Step Reasoning
Start: empty list
After inserting 5:
- list is
5
After inserting 1 at the front:
- list is
1 -> 5
After inserting 2 at the front:
- list is
2 -> 1 -> 5
After inserting 3 at the front:
- list is
3 -> 2 -> 1 -> 5
After inserting 8 at the front:
- list is
8 -> 3 -> 2 -> 1 -> 5
Now OutputLinkedList() traverses from FirstNode, so it prints:
83215
If your program uses print() on each item, each value appears on a separate line, which is what is shown in the answer.
Key Takeaways
- Front insertion reverses the order of entry.
- To predict output, trace the logical linked-list order, not the array row order.
- Linked-list traversal prints in pointer order.
Common Mistakes
- Writing the output as
5 1 2 3 8, which ignores the front-insertion rule. - Assuming the array rows stay in sorted or input order.
- Forgetting that the output procedure follows pointers from the head.
Things to Be Careful About
- The important thing is the order of values:
8, 3, 2, 1, 5. - Exact line formatting depends on how
printis used; with the given procedure, each value is on a new line. - Do not include the input values themselves as part of the output unless your console echoes them.
The procedure RemoveData() removes a node from the linked list.
The procedure takes the data item to be removed from the linked list as a parameter.
The procedure checks each node in the linked list, starting with the node FirstNode, until it finds the node to be removed. This node is added to the empty list, and pointers are changed as appropriate. The procedure only removes the first occurrence of the parameter.
Assume that the data item being removed is in the linked list.
Write program code for RemoveData().
Save your program.
Copy and paste the program code into part 3(d)(i) in the evidence document.
Answer
def RemoveData(ItemToRemove):
global LinkedList, FirstEmpty, FirstNode
CurrentNode = FirstNode
PreviousNode = -1
while LinkedList[CurrentNode][0] != ItemToRemove:
PreviousNode = CurrentNode
CurrentNode = LinkedList[CurrentNode][1]
if PreviousNode == -1:
FirstNode = LinkedList[CurrentNode][1]
else:
LinkedList[PreviousNode][1] = LinkedList[CurrentNode][1]
LinkedList[CurrentNode][0] = -1
LinkedList[CurrentNode][1] = FirstEmpty
FirstEmpty = CurrentNode
See program code
Background Concept
Removing a node from a linked list has two distinct jobs:
- detach the node from the real data list
- return that node to the free list so it can be reused later
In an array-based linked list, you do this by changing indices rather than memory addresses, but the logic is the same.
A removal procedure usually needs:
- a
CurrentNodepointer for the node being checked - a
PreviousNodepointer so you can reconnect the list when the target is found
There is also a special case when the node to remove is the first node in the list, because there is no previous node before the head.
Understanding the Question
The procedure receives a data value to remove. You are told to assume that the value is definitely in the list, and only the first occurrence should be removed.
Once found:
- it must be removed from the linked list
- it must be added to the empty list
- pointers must be updated correctly
So this is not just a search question; it is a search plus pointer repair question.
Approach
Use a traversal with two variables:
- start
CurrentNodeatFirstNode - start
PreviousNodeat-1 - move through the list until the data matches the item to remove
- if
PreviousNode == -1, the first node is being removed, so moveFirstNodeon - otherwise, make
PreviousNodeskip overCurrentNode - clear the removed node's data
- add that node to the front of the free list by pointing it to the old
FirstEmpty - update
FirstEmpty
This removes the first matching node and preserves both linked structures.
Step-by-Step Reasoning
CurrentNode = FirstNode
- Begin at the start of the real linked list.
PreviousNode = -1
- At the head, there is no node before the current one.
- Using
-1marks that special situation.
while LinkedList[CurrentNode][0] != ItemToRemove:
- Keep moving until the required data value is found.
- Because the question says to assume the item exists, no extra not-found check is needed.
Inside the loop:
PreviousNode = CurrentNodeCurrentNode = LinkedList[CurrentNode][1]
This shifts both pointers forward by one node.
When the loop ends, CurrentNode is the node that must be removed.
Case 1: removing the first node
if PreviousNode == -1:
FirstNode = LinkedList[CurrentNode][1]
- If the node to remove is the head, the new first node becomes whatever the old head pointed to.
Case 2: removing a later node
else:
LinkedList[PreviousNode][1] = LinkedList[CurrentNode][1]
- This makes the previous node point directly to the node after the removed node.
- So
CurrentNodeis bypassed and no longer part of the real list.
Return removed node to free list
LinkedList[CurrentNode][0] = -1
- Mark the node as empty again.
LinkedList[CurrentNode][1] = FirstEmpty
- Link the removed node to the current start of the free list.
FirstEmpty = CurrentNode
- The removed node now becomes the first free node.
This is a neat and efficient way to recycle deleted nodes.
Key Takeaways
- Linked-list deletion usually needs both current and previous pointers.
- Removing the head is a special case.
- In an array-based linked list, deleted nodes should be returned to the free list.
- Pointer updates must preserve both the real list and the empty list.
Common Mistakes
- Forgetting the head-node special case and always trying to update a previous node.
- Removing the node from the data list but not adding it back to the free list.
- Forgetting to set the removed node's data back to
-1. - Continuing to search after the first match, even though only the first occurrence should be removed.
- Overwriting a pointer before using its value, which loses part of the list.
Things to Be Careful About
PreviousNode == -1means the match was at the head.- The statement assumes the item exists, so this version does not need a not-found condition.
- The order of updates matters: detach from the data list first, then add to the free list.
- Make sure
FirstNodeandFirstEmptyare both updated where needed.
Amend the main program to:
- call
RemoveData()with the parameter 5 - output the word "After"
- call
OutputLinkedList().
Save your program.
Copy and paste the program code into part 3(d)(ii) in the evidence document.
Answer
RemoveData(5)
print("After")
OutputLinkedList()
See program code
Background Concept
A main program often performs a sequence of operations on the same data structure. Here, the linked list is first built and displayed, then one item is removed, then the list is displayed again so the change can be seen.
Passing a value in a procedure call, such as RemoveData(5), provides the procedure with the item it must act on.
Understanding the Question
This part says to amend the main program so that after the earlier insertion and output steps, it should:
- remove the value
5 - print the word
After - output the linked list again
So these lines are added after the original calls from part 3(c)(ii).
Approach
Add three statements in sequence:
- delete
5 - print a label so the second output is clearly separated
- traverse and display the updated linked list
Step-by-Step Reasoning
RemoveData(5)
- Calls the removal procedure with the parameter value
5. - The first occurrence of
5in the linked list is removed.
print("After")
- Outputs the word
Afterexactly as requested. - This helps distinguish the second list from the first one.
OutputLinkedList()
- Traverses the updated linked list and prints its contents after the deletion.
These lines belong in the main program after the original insert and first output steps.
Key Takeaways
- Main programs coordinate multiple operations on the same structure.
- A procedure call can include an argument value.
- Extra output such as
Aftercan make test results clearer.
Common Mistakes
- Writing
RemoveData()with no parameter. - Printing
Afterbefore the deletion call. - Replacing the earlier output instead of adding this second output afterwards.
Things to Be Careful About
- The exact required parameter is
5. - The word should be printed as
After. - These are additional lines in the main program, not changes inside
OutputLinkedList()orRemoveData().
Test your program with both sets of given test data:
Test data set 1: 5 6 8 9 5
Test data set 2: 10 7 8 5 6
Take a screenshot of each output.
Save your program.
Copy and paste the screenshot(s) into part 3(d)(iii) in the evidence document.
Answer
For test data set 1: 5 6 8 9 5
5
9
8
6
5
After
9
8
6
5
For test data set 2: 10 7 8 5 6
6
5
8
7
10
After
6
8
7
10
See expected output
Background Concept
Two ideas are being tested together here:
- front insertion reverses the order of entry
- deletion removes only the first occurrence found during traversal from
FirstNode
Because the search starts at the head of the list, "first occurrence" means the first matching node encountered in linked-list order, not the first value typed by the user.
Understanding the Question
You must test the full amended program from the previous parts. That means the output includes:
- the list after the five insertions
- the word
After - the list after
RemoveData(5)has been applied
You must do this for both given input sets.
Approach
For each data set:
- build the linked list by inserting each value at the front
- write down the first output
- remove the first
5encountered from the head onwards - write down the second output after
After
The two data sets are useful because they test different removal situations:
- set 1 removes a
5at the head - set 2 removes a
5from the middle
Step-by-Step Reasoning
Test data set 1: 5 6 8 9 5
Insert at front each time:
- after
5:5 - after
6:6 -> 5 - after
8:8 -> 6 -> 5 - after
9:9 -> 8 -> 6 -> 5 - after final
5:5 -> 9 -> 8 -> 6 -> 5
So the first output is:
59865
Now remove the first occurrence of 5.
- The head already contains
5, so that is the node removed. - The new list becomes
9 -> 8 -> 6 -> 5.
So after printing After, the second output is:
9865
Test data set 2: 10 7 8 5 6
Insert at front each time:
- after
10:10 - after
7:7 -> 10 - after
8:8 -> 7 -> 10 - after
5:5 -> 8 -> 7 -> 10 - after
6:6 -> 5 -> 8 -> 7 -> 10
So the first output is:
658710
Now remove the first occurrence of 5.
- The head is
6, so keep moving. - The next node is
5, so that node is removed. - The new list becomes
6 -> 8 -> 7 -> 10.
So after printing After, the second output is:
68710
Key Takeaways
- Front insertion reverses input order.
- The first occurrence removed is the first match in traversal order.
- Different test data can check different pointer cases, such as deleting the head and deleting a middle node.
- Tracing by hand is an important practical programming skill.
Common Mistakes
- Keeping the values in input order instead of reversing them through front insertion.
- Removing the last
5instead of the first one found from the head. - Forgetting that the first output still appears before the word
After. - Assuming both test sets remove a head node.
Things to Be Careful About
- Test data set 1 contains two
5values; only the first one in list order is removed. - Test data set 2 removes a middle node, so the head
6stays in place. - If your
printlayout differs slightly, the important marking point is the correct sequence of values before and after removal.
