Computer Science 9618/41 — May/June 2021
Cambridge A-Level · Practical · worked solutions for every part, with the mark scheme
Topics Programming Paradigms (Procedural and Object-oriented) · Algorithms and Abstract Data Types · File Processing and Exception Handling
An unordered linked list uses a 1D array to store the data.
Each item in the linked list is of a record type, node, with a field data and a field nextNode.
The current contents of the linked list are:
The following is pseudocode for the record type node.
TYPE node
DECLARE data : INTEGER
DECLARE nextNode : INTEGER
ENDTYPE
Write program code to declare the record type node.
Save your program as question1.
Copy and paste the program code into part 1(a) in the evidence document.
Answer
class node:
def __init__(self, data, nextNode):
self.data = data
self.nextNode = nextNode
See program code
Background Concept
A record type is a user-defined data structure that stores several related fields together. In the pseudocode, a node record has two integer fields: data and nextNode. In Python, the closest direct way to represent this is with a class, where each object stores those two values as attributes.
For this question, each array element is not just one number. It must hold both the value stored in the node and the pointer to the next node. That is why a record or class is needed.
Understanding the Question
You are given pseudocode for a record type called node:
datastores the value in the nodenextNodestores the index of the next node in the array-based linked list
The task is only to declare this type in program code. It is not asking for the array yet, and it is not asking for any linked-list operations yet.
Approach
In Python, declare a class called node and give it a constructor. The constructor should accept the two pieces of information each node needs and assign them to the object.
So the structure is:
- define the class
node - define
__init__ - receive
dataandnextNode - save them as
self.dataandself.nextNode
Step-by-Step Reasoning
class node: creates a new user-defined type with the exact identifier the question uses.
def __init__(self, data, nextNode): defines the constructor. This runs whenever a new node object is created. The parameters match the two fields in the pseudocode record.
self.data = data stores the node's data value inside the object.
self.nextNode = nextNode stores the pointer value inside the object.
That is all that is needed here. The question only asks for the declaration of the record type, so no extra methods are required.
Key Takeaways
- A record type in pseudocode can be represented by a class in Python.
- Each field in the record becomes an attribute of the class.
- A constructor is the cleanest way to create each node with its two values already set.
Common Mistakes
- Omitting the constructor completely, so objects cannot easily be initialised with both fields.
- Writing
data = datainstead ofself.data = data; that does not create an attribute in the object. - Changing the field names, for example using
nextinstead ofnextNode; the exam expects the given identifiers to be used consistently.
Things to Be Careful About
- Use the exact field name
nextNode. - Keep the class identifier as
nodeto match the question. - Remember that
selfis needed in Python to refer to the object's own attributes.
Write program code for the main program.
Declare a 1D array of type node with the identifier linkedList, and initialise it with the data shown in the table on page 2. Declare the pointers.
Save your program.
Copy and paste the program code into part 1(b) in the evidence document.
Answer
linkedList = [
node(1, 1),
node(5, 4),
node(6, 7),
node(7, -1),
node(2, 2),
node(0, 6),
node(0, 8),
node(56, 3),
node(0, 9),
node(0, -1)
]
startPointer = 0
emptyList = 5
See program code
Background Concept
An array-based linked list stores nodes in a normal array, but each node also contains a pointer field. Instead of using memory addresses, nextNode stores the array index of the next node. A value of -1 means there is no next node.
There are usually two important pointers:
startPointerpoints to the first used node in the linked listemptyListpoints to the first free node in the list of unused spaces
So the array contains both the active linked list and the free-list of unused nodes.
Understanding the Question
You must write the main program declarations for:
- a 1D array called
linkedList - each element being of type
node - all values initialised exactly as shown in Fig. 1.1
- the pointer variables also declared and initialised
The diagram gives the complete state of memory, so the job is to copy that state faithfully into code.
Approach
Create a Python list where each element is a node object. The object at each position must match the diagram's row for that index.
Then assign:
startPointer = 0emptyList = 5
That reproduces the exact linked-list structure shown.
Step-by-Step Reasoning
Index 0 stores data 1 and next pointer 1, so the first element is node(1, 1).
Index 1 stores 5 and 4, so the second element is node(5, 4).
Continue this row by row until all 10 array positions have been created:
- index 2:
node(6, 7) - index 3:
node(7, -1) - index 4:
node(2, 2) - index 5:
node(0, 6) - index 6:
node(0, 8) - index 7:
node(56, 3) - index 8:
node(0, 9) - index 9:
node(0, -1)
After that, set the pointers.
startPointer = 0 means the used linked list begins at index 0.
If you follow the used nodes, the chain is:
0 -> 1 -> 4 -> 2 -> 7 -> 3 -> -1
So the data in the current linked list is 1, 5, 2, 6, 56, 7.
emptyList = 5 means the free nodes begin at index 5. Following those pointers gives the free-list:
5 -> 6 -> 8 -> 9 -> -1
That structure is important later when a new node is added.
Key Takeaways
- In an array-based linked list, the array position acts like the node's location in memory.
nextNodelinks one index to the next.startPointeridentifies the used list, whileemptyListidentifies the unused nodes.
Common Mistakes
- Copying a row into the wrong index position.
- Mixing up
dataandnextNodevalues. - Forgetting to initialise one of the pointers.
- Using
Noneinstead of-1when the given structure clearly uses-1as the null marker.
Things to Be Careful About
- The array must be named exactly
linkedList. - The nodes at indices 5, 6, 8 and 9 are not part of the current data list; they are free nodes.
startPointerandemptyListare different and must not be confused.- Keep the row order exactly the same as the diagram so later procedures traverse the correct structure.
The procedure outputNodes() takes the array and startPointer as parameters. The procedure outputs the data from the linked list by following the nextNode values.
Write program code for the procedure outputNodes().
Save your program.
Copy and paste the program code into part 1(c)(i) in the evidence document.
Answer
def outputNodes(linkedList, startPointer):
currentPointer = startPointer
while currentPointer != -1:
print(linkedList[currentPointer].data)
currentPointer = linkedList[currentPointer].nextNode
See program code
Background Concept
To output the contents of a linked list, you do not loop through the array from index 0 to the end. Instead, you start at the first used node and repeatedly follow each node's pointer to the next one.
In an array-based linked list:
startPointergives the index of the first node- each node's
nextNodegives the index of the next node -1means the end of the list
This is called traversal.
Understanding the Question
The procedure outputNodes() is given two parameters:
- the array
linkedList - the
startPointer
It must output the data values that are actually in the linked list. That means it must follow the chain of nextNode values, not just print every element in the array.
Approach
Use a variable such as currentPointer.
- set it to
startPointer - while it is not
-1 - output the node's
data - move to the next node using
nextNode
This is the standard traversal pattern for a linked list.
Step-by-Step Reasoning
def outputNodes(linkedList, startPointer): defines a procedure that receives the array and the first node position.
currentPointer = startPointer means traversal begins at the first used node.
while currentPointer != -1: keeps processing nodes until the end of the list is reached.
Inside the loop:
print(linkedList[currentPointer].data)outputs the data in the current nodecurrentPointer = linkedList[currentPointer].nextNodemoves to the next node in the chain
For the given data, the movement would be:
- start at index 0, output
1 - move to 1, output
5 - move to 4, output
2 - move to 2, output
6 - move to 7, output
56 - move to 3, output
7 - move to
-1, stop
That shows why the values come out in linked-list order rather than numerical index order.
Key Takeaways
- Linked lists are processed by following pointers.
- The array index order is not necessarily the logical order of the list.
- The termination condition is the null marker, here
-1.
Common Mistakes
- Using a
forloop through all array positions; that would print unused nodes too. - Starting at index 0 by assumption instead of using
startPointer. - Forgetting to update
currentPointer, causing an infinite loop. - Stopping at the wrong condition, such as when
data == 0instead of whennextNodereaches-1.
Things to Be Careful About
- Use
linkedList[currentPointer].nextNodeto move along the list. - Use
-1as the end marker because that is what the structure uses. - Output
data, notnextNode. - This procedure should work for any valid
startPointer, not just the specific example shown.
Edit the main program to call the procedure outputNodes().
Take a screenshot to show the output of the procedure outputNodes().
Save your program.
Copy and paste the screenshot into part 1(c)(ii) in the evidence document.
Answer
Add this call in the main program:
outputNodes(linkedList, startPointer)
Expected output:
1
5
2
6
56
7
1 5 2 6 56 7
Background Concept
When a traversal procedure is called on a linked list, the output sequence depends on the links between nodes, not on the physical array positions. The startPointer tells you where to begin, and each nextNode tells you where to go next.
Understanding the Question
This part asks you to edit the main program so that outputNodes() is called, then show what appears on the screen. There is no new algorithm here; the main task is to use the procedure from part (c)(i) and determine the correct output.
Approach
Call:
outputNodes(linkedList, startPointer)
Then follow the linked-list chain from the given structure to work out what the procedure prints.
Step-by-Step Reasoning
The start of the list is index 0 because startPointer = 0.
Now follow the links:
- index 0 has data
1, next1 - index 1 has data
5, next4 - index 4 has data
2, next2 - index 2 has data
6, next7 - index 7 has data
56, next3 - index 3 has data
7, next-1
So the output order is:
1, 5, 2, 6, 56, 7
With the procedure as written, each value is printed on its own line.
Key Takeaways
- The visible order of output comes from pointer links.
- Always trace from
startPointerto-1. - Screenshot-style questions still depend on correct algorithm tracing.
Common Mistakes
- Printing in array index order:
1, 5, 6, 7, 2, ...which is wrong. - Including free-list nodes such as indices 5, 6, 8 and 9.
- Forgetting that index 4 comes before index 2 in the linked structure.
Things to Be Careful About
- The screenshot should show only the active linked-list data.
- If your own program prints labels or extra text, your screenshot may differ in formatting, but the data sequence must still be correct.
- The actual values must be
1 5 2 6 56 7in that order.
The function, addNode(), takes the linked list and pointers as parameters, then takes as input the data to be added to the end of the linkedList.
The function adds the node in the next available space, updates the pointers and returns True. If there are no empty nodes, it returns False.
Write program code for the function addNode().
Save your program.
Copy and paste the program code into part 1(d)(i) in the evidence document.
Answer
def addNode(linkedList, startPointer, emptyList):
newData = int(input())
if emptyList == -1:
return False, startPointer, emptyList
newPointer = emptyList
emptyList = linkedList[emptyList].nextNode
linkedList[newPointer].data = newData
linkedList[newPointer].nextNode = -1
if startPointer == -1:
startPointer = newPointer
else:
currentPointer = startPointer
while linkedList[currentPointer].nextNode != -1:
currentPointer = linkedList[currentPointer].nextNode
linkedList[currentPointer].nextNode = newPointer
return True, startPointer, emptyList
See program code
Background Concept
In an array-based linked list, unused nodes are often stored as a second linked list called the free-list. The pointer emptyList points to the first available empty node. When a new node is needed, the program removes the first node from the free-list and uses it.
To add a node at the end of the used linked list, the program must:
- check whether a free node exists
- take one node from the free-list
- place the new data into that node
- mark its
nextNodeas-1because it will become the new last node - find the current last node in the used list
- change that last node's
nextNodeto point to the new node
Understanding the Question
The function addNode() must:
- take the array and pointers as parameters
- input the data value to be added
- add a new node at the end of the linked list
- update the pointers correctly
- return a success result if it worked, otherwise return failure if no empty node is available
So this is not just a normal append to a Python list. It must preserve the linked-list structure shown in the array representation.
Approach
Use the free-list first.
- If
emptyList == -1, there is no spare node, so the function fails. - Otherwise, use the node at
emptyListas the new node. - Advance
emptyListto the next free position. - Store the input data in the chosen node and set its
nextNodeto-1. - Find the current last used node.
- Point that last node to the new node.
- Return success.
In Python, integers such as startPointer and emptyList are not passed by reference in the same way as some other languages, so returning the updated pointers alongside the Boolean result is a practical way to keep the caller updated.
Step-by-Step Reasoning
newData = int(input()) reads the value that will be added.
if emptyList == -1: checks whether there are any free nodes. If not, the function cannot insert anything, so it returns failure immediately.
newPointer = emptyList remembers the index of the free node we are about to use.
emptyList = linkedList[emptyList].nextNode moves the free-list pointer on to the next free node. This removes newPointer from the free-list.
Now the selected free node becomes a real used node:
linkedList[newPointer].data = newDatalinkedList[newPointer].nextNode = -1
Setting nextNode to -1 is important because the new node is being added at the end.
If the used list were empty, startPointer would need to become newPointer. That is why the code checks if startPointer == -1:.
Otherwise, the list already has nodes, so the code traverses from the start until it finds the current last node, meaning the node whose nextNode is -1.
linkedList[currentPointer].nextNode = newPointer then connects the old last node to the new one.
Finally, the function returns success together with the updated pointers.
Key Takeaways
- Adding to an array-based linked list uses both the used list and the free-list.
emptyListmust be updated when a free node is taken.- Appending requires changing the previous last node's pointer.
- A success/failure return value is useful when insertion may be impossible.
Common Mistakes
- Forgetting to move
emptyListon to the next free node. - Forgetting to set the new node's
nextNodeto-1. - Overwriting the new node before saving its index.
- Traversing until
currentPointer == -1instead of stopping at the last valid node. - Returning success without actually linking the node into the list.
Things to Be Careful About
- The free-list null marker is
-1, not0. - The node taken from the free-list must be removed from the free-list before being reused.
- If you are writing Python, make sure the caller receives updated pointer values after the function call.
- Appending to the end means changing the old tail node's
nextNode, not changingstartPointerunless the list was empty.
Edit the main program to:
- call
addNode() - output an appropriate message depending on the result returned from
addNode() - call
outputNodes()twice; once before callingaddNode()and once after callingaddNode().
Save your program.
Copy and paste the program code into part 1(d)(ii) in the evidence document.
Answer
print("Before:")
outputNodes(linkedList, startPointer)
result, startPointer, emptyList = addNode(linkedList, startPointer, emptyList)
if result:
print("Node added")
else:
print("No empty node available")
print("After:")
outputNodes(linkedList, startPointer)
See program code
Background Concept
A main program often coordinates smaller procedures and functions. A procedure such as outputNodes() performs an action, while a function such as addNode() performs an action and also returns a result. Here the returned result is used to decide which message to display.
Understanding the Question
You must edit the main program so that it:
- outputs the current linked list
- calls
addNode() - prints a suitable message depending on whether the addition succeeded
- outputs the linked list again afterwards
So the important skill here is sequencing the calls in the correct order.
Approach
The required order is exactly:
- call
outputNodes()before insertion - call
addNode() - use
ifto display the success or failure message - call
outputNodes()again after insertion
This makes the effect of the insertion visible.
Step-by-Step Reasoning
print("Before:") is a helpful label so the first traversal output is clearly identified.
outputNodes(linkedList, startPointer) shows the current list before any change is made.
result, startPointer, emptyList = addNode(...) calls the insertion function and stores the returned values. The Boolean result goes into result, while the pointers are updated for later use.
The if result: statement checks whether the insertion worked:
- if
True, outputNode added - otherwise, output
No empty node available
print("After:") labels the second list display.
outputNodes(linkedList, startPointer) is then called again so the user can see the list after the attempted insertion.
This exactly matches the wording of the question because the list is shown once before and once after the call to addNode().
Key Takeaways
- Procedures and functions are often combined in a main control sequence.
- A returned Boolean is commonly used with an
ifstatement to choose output. - Showing data before and after an update is a useful testing technique.
Common Mistakes
- Calling
outputNodes()only once. - Printing the message before calling
addNode(). - Forgetting to store the returned updated pointers.
- Using the wrong condition for the success message.
Things to Be Careful About
- Keep the calls in the order the question specifies.
- Make sure the second
outputNodes()call uses the updated linked list state. - If your function returns updated pointers in Python, the assignment when calling it must match that return structure exactly.
Test your program by inputting the data value 5 and take a screenshot to show the output.
Save your program.
Copy and paste the screenshot into part 1(d)(iii) in the evidence document.
Answer
With input 5, the expected output is:
Before:
1
5
2
6
56
7
Node added
After:
1
5
2
6
56
7
5
Before: 1 5 2 6 56 7 | Node added | After: 1 5 2 6 56 7 5
Background Concept
Testing a linked-list operation means tracing both the pointer updates and the visible output. When a node is appended, the logical order of the list changes even though most of the array stays the same.
The free-list is especially important here because the new node must come from the first available empty position.
Understanding the Question
You must run the finished program with input value 5 and show the output. To work that out, you need to know both:
- what the list looks like before insertion
- what changes after
addNode()uses the next free node
Approach
Trace the insertion step by step.
- output the original list
- add value
5using the first free node - update the free-list and the last used node's pointer
- output the list again
That gives the exact sequence that should appear in the screenshot.
Step-by-Step Reasoning
Before insertion, the linked list is:
0 -> 1 -> 4 -> 2 -> 7 -> 3 -> -1
So the data output before the call is:
- 1
- 5
- 2
- 6
- 56
- 7
Now trace addNode() with input 5.
The first free node is at index 5 because emptyList = 5.
So the new node will use index 5.
Then emptyList is updated to the next free node, which is index 6, because index 5 originally had nextNode = 6.
The new node at index 5 is changed to:
data = 5nextNode = -1
Now the current last used node must be found. Before insertion, the tail is index 3 because it has nextNode = -1.
So index 3 is updated so that nextNode = 5.
The used list is now:
0 -> 1 -> 4 -> 2 -> 7 -> 3 -> 5 -> -1
Therefore the second traversal outputs:
- 1
- 5
- 2
- 6
- 56
- 7
- 5
Since a free node was available, the message shown is Node added.
Key Takeaways
- Testing linked-list code often means tracing pointer changes manually.
- Appending adds the new value at the end of the logical list, not at the next numerical index in display order.
- The free-list head moves on after a node is allocated.
Common Mistakes
- Assuming the new value replaces an existing
5rather than being appended as a new node. - Forgetting that the original list is displayed before the insertion happens.
- Updating the free-list incorrectly and choosing the wrong index for the new node.
- Missing the final
5at the end of the second output.
Things to Be Careful About
- The value entered is
5, but there is already a5in the list; duplicates are allowed because the list is unordered. - The first output is unchanged from the original list.
- The new node uses index 5 only because that is where
emptyListpoints initially. - If your own program includes an input prompt, the exact screenshot formatting may vary, but the list content and success message must still be correct.
The rest of this paper
2 more questions- Q2Programming Paradigms (Procedural and Object-oriented) · Algorithms and Abstract Data Types20M
- Q3Programming Paradigms (Procedural and Object-oriented) · File Processing and Exception Handling31M
