AS & A Level Computer Science · Area 1 · AS Level content

Data types, arrays, files and abstract data types

Choosing a data type, grouping fields into a record, processing arrays with a linear search or a bubble sort, and moving data in and out of a text file are the four skills this section tests, and they are examined by asking you to write pseudocode rather than to describe it. You lose marks here mainly through small mechanical slips: a loop that runs past the end of an array, a swap with no temporary variable, a read that happens before the end-of-file test, or a condition that only half covers the case. The abstract data types are marked more generously, but only if you name the operations and justify the order the situation needs instead of simply naming the structure.

Not started

Syllabus coverage

  • Data types: integer, real, char, string, Boolean and date, with pseudocode types INTEGER, REAL, CHAR, STRING, BOOLEAN, DATE, ARRAY and FILE
  • Select and use appropriate data types for a problem solution
  • Show understanding of the purpose of a record structure, to hold a set of data of different data types under one identifier
  • Write pseudocode to define a record structure, and to read data from and save data to a record structure
  • Use the technical terms associated with arrays, including index, upper bound and lower bound
  • Select a suitable data structure (1D or 2D array) for a given task and write pseudocode for 1D and 2D arrays
  • Write pseudocode to process array data, sorting using a bubble sort
  • Write pseudocode to process array data, searching using a linear search
  • Show understanding of why files are needed
  • Write pseudocode to handle text files that consist of one or more lines
  • Show understanding that an abstract data type (ADT) is a collection of data and a set of operations on those data
  • Show understanding that a stack, a queue and a linked list are examples of ADTs, and describe their key features
  • Justify the use of a stack, a queue or a linked list for a given situation, and add, edit and delete data in them
  • Describe how a queue, a stack and a linked list can be implemented using arrays

Part of the AS & A Level Computer Science syllabus 9618.

Data types and how to choose one

Every value in a program is declared with a data type, and the type fixes what the value can be and which operations are allowed on it. Cambridge pseudocode uses six simple data types, INTEGER, REAL, CHAR, STRING, BOOLEAN and DATE, plus the structured types ARRAY and FILE. Choosing the type is not decoration: it decides whether arithmetic will work, whether a leading zero survives, and whether the value can be validated at all.

INTEGER holds whole numbers, positive, negative or zero, and is used for counting: a quantity, a year group, the number of seats sold. REAL holds a number with a fractional part, such as a price, an average or a measurement, and it must be used whenever the result of a calculation can have a decimal part. Money is sometimes stored as an INTEGER number of pence rather than a REAL number of pounds, because binary floating point cannot store every decimal fraction exactly, so repeated additions of 0.10 can drift.

CHAR holds exactly one character, such as a grade letter or a single initial. STRING holds zero or more characters: a name, an address, a postcode, a telephone number. A telephone number and a postcode look numeric but must be STRING, because a postcode contains letters and spaces and a telephone number beginning with 0 would lose that zero if it were stored as an INTEGER.

BOOLEAN holds only TRUE or FALSE, and it is the right type for anything with two states: whether a pupil is registered, whether a book is on loan, whether a password has been entered correctly. DATE holds a calendar date, and it exists so that dates can be compared and used in calculations. The number of days between two DATE values can be calculated, which is how an age or an overdue charge is worked out; a date stored as a STRING can be displayed but cannot be treated as a date without being converted first.

  • Ask four questions of the data: is it counted (INTEGER), measured or money with a fractional part (REAL), a single character (CHAR), text (STRING), one of two states (BOOLEAN) or a calendar date (DATE)?
  • Use STRING, not INTEGER, for anything with a leading zero or with letters in it: telephone numbers, postcodes, product codes such as 0071.
  • Use REAL only where a fractional part is genuinely possible, and INTEGER where it never is, because the type tells the reader what the value means.
  • A value that is only ever compared with another date, or used to work out an interval, should be DATE rather than STRING.
Data typeWhat it can holdTypical useWhy another type would be wrong
INTEGERA whole number, positive, negative or zeroNumber of tickets sold, year group, stock levelA REAL would allow impossible values such as 2.5 tickets; a STRING could not be added up
REALA number with a fractional partPrice, average mark, mass in kilogramsAn INTEGER would truncate the fraction and lose precision
CHARExactly one characterGrade letter, middle initial, Y or NA STRING allows more than one character, so the length is no longer guaranteed
STRINGZero or more charactersName, address, postcode, telephone numberAn INTEGER loses a leading zero and cannot hold letters or spaces
BOOLEANTRUE or FALSE onlyIs the book on loan?, has the fee been paid?A STRING invites inconsistent answers such as yes, Y or true
DATEA calendar dateDate of birth, date borrowed, date of the next serviceA STRING can be displayed but cannot be compared or subtracted as a date
In a question that asks you to justify a data type, the mark is for the reason tied to the data, not for the name. 'DATE, because the number of days overdue is found by subtracting the borrow date from today' scores; a bare 'DATE' does not.

Records: different data types under one identifier

A record is a structure that holds a set of related data of different data types under one identifier. One pupil's record might hold a last name (STRING), a date of birth (DATE), a year group (INTEGER) and whether the fees are paid (BOOLEAN). Without records you would need four separate variables for every pupil and a naming scheme to keep them together; with a record the four fields travel as one item, which can be passed to a procedure or written to a file in one go.

The structure is defined once, and the definition is a type, not a variable. TYPE names the record type and lists each field with its data type, and ENDTYPE closes the definition. A variable of that type is then declared in the usual way, and each field is reached using the variable name, a dot and the field name, for example Pupil.YearGroup.

This dot notation is accepted anywhere a variable is accepted: INPUT Pupil.LastName reads a value into the field, an assignment stores one, and OUTPUT displays one. Because a field is just a named storage location with a type, it behaves like any other variable: LastName can be compared with another STRING, DateOfBirth can be compared with today's date, and FeePaid can be used directly as a condition. A whole record can also be read from a file or written to a file in one statement when the file stores records, which is why records and files are taught together.

Records combine naturally with arrays. DECLARE Pupils : ARRAY[1:200] OF StudentRecord gives 200 records, and Pupils[7].LastName is the surname of the seventh pupil. Parallel arrays, one array of names and a separate array of marks, also work, but they can drift out of step the moment a pupil is deleted from one array and not the other; an array of records cannot drift, because element 7 holds every field belonging to pupil 7.

Line of pseudocodeWhat it does
TYPE StudentRecordStarts the definition of a record type; the name is chosen by the programmer
DECLARE LastName : STRINGDeclares a field inside the record, with its own data type
DECLARE DateOfBirth : DATEA second field, of a different type, inside the same record
DECLARE YearGroup : INTEGERA third field; a record may hold as many fields as the problem needs
ENDTYPEEnds the definition. No variable exists yet
DECLARE Pupil : StudentRecordDeclares one variable of the record type
Pupil.LastName <- "Okafor"Assigns a value to one field using dot notation
INPUT Pupil.YearGroupReads a value straight into a field
OUTPUT Pupil.LastName, Pupil.YearGroupOutputs fields exactly like ordinary variables
Do not write a record as though ENDTYPE declared it: the type definition and the declaration of a variable of that type are two separate statements, and examiners award a mark for each.

1D arrays: index, bounds and processing

An array is a fixed number of elements of the same data type, held under one identifier and selected by an index (also called a subscript). The lower bound is the first index the array allows and the upper bound is the last, so ARRAY[1:30] has 30 elements and ARRAY[0:9] has 10. The number of elements is the upper bound minus the lower bound plus one.

Declaring an array reserves all of its storage at once: DECLARE Scores : ARRAY[1:30] OF INTEGER creates 30 integer locations, all initially undefined. An element is used exactly like a variable, with the index in square brackets, so Scores[4] <- 67 stores a mark and OUTPUT Scores[4] displays it. The index may be a variable or an expression, and that is what makes an array worth having: Scores[Index] inside a loop reaches every element in turn with a single statement.

Most array processing is one pass from the lower bound to the upper bound. A FOR loop reads the values, and a second pass totals, counts, finds the largest or works out an average. A running total must be set to 0 before the loop, never inside it, and a maximum must be initialised to the first element or to a value below any possible element, otherwise it stays at 0 and no mark is ever larger than it.

Indexing outside the declared bounds is an error. Some languages stop the program with an index error, others silently write over another variable, which is far worse because the fault appears somewhere else. Arrays are also fixed in size, so when the number of items is not known in advance the array is declared generously and a separate variable counts how many elements are actually in use.

TermMeaningExample in ARRAY[1:50] OF STRING
ElementOne storage location in the arrayNames[1] is the first element
Index (subscript)The value in brackets that selects an elementNames[Index] where Index is 7 selects the seventh
Lower boundThe smallest index allowed1
Upper boundThe largest index allowed50
SizeThe number of elements, upper minus lower plus one50 elements
DimensionThe number of indices needed to select an elementOne, so this is a 1D array
A FOR loop from the lower bound to the upper bound is the safest way to touch every element, because the bounds are written into the declaration and the loop cannot run past them.

2D arrays: data in a grid

A 2D array needs two indices to select an element, and it is the natural structure for anything laid out in rows and columns. DECLARE Seats : ARRAY[1:5, 1:3] OF BOOLEAN declares 5 rows of 3 columns, which is 15 elements, and Seats[2, 3] is the element in row 2, column 3. The first index is always the row and the second is the column, and that order has to be kept the same everywhere in the program, because Seats[3, 2] is a different element.

Reading or processing the whole array takes two loops, one inside the other. The outer loop steps through the rows and the inner loop steps through every column of the current row, so the inner loop finishes before the row changes. Reading a value into each element therefore takes one INPUT inside the inner loop, with both indices written in the brackets, and a total over the grid is a sum accumulated in the inner loop.

A 2D array is the right choice when each item is described by two facts: a seat by its row and its number, a mark by the pupil and the subject, a pixel by its x and y coordinates, a square on a board by its row and column. A 1D array is right when one index is enough, as with a list of 30 names, even though each name is itself a string of characters.

Two errors are common. The first is mixing the order of the indices so that the program reads columns where it means rows; the second is declaring an array far larger than the data, which wastes storage and makes the loops harder to follow. Decide which index is the row before writing a single line, and use the same meaning throughout.

DeclarationElementsWhat it models
ARRAY[1:30] OF STRING30A list of names; one index is enough
ARRAY[1:5, 1:3] OF BOOLEAN15A seating plan, 5 rows of 3 seats, TRUE if the seat is booked
ARRAY[1:20, 1:8] OF INTEGER160A marks table, 20 pupils by 8 subjects
ARRAY[1:8, 1:8] OF CHAR64A chessboard or draughts board
ARRAY[1:100, 1:100] OF INTEGER10 000An image, where each element is the colour of one pixel
If a question describes two facts per item, such as pupil and subject, the answer is a 2D array; if it describes one fact per item, a 1D array is enough and a 2D array would be wrong.

Searching an array with a linear search

A linear search (serial search) checks the elements one at a time, starting at the lower bound, until it finds the value it is looking for or runs past the last element. It makes no demand that the data be in order, which is its great strength: it is the only one of the two searches in this syllabus that works on unsorted data, and it can be used the moment a value is stored.

The algorithm needs four things: the array, the value being searched for, an index that steps through the elements, and a Boolean flag that records whether the value has been found. The index and the flag are both initialised before the loop, and the loop continues while the value has not been found and the index is still inside the array. Missing either half of that condition breaks the algorithm: without the flag it never stops when the value is found, and without the bounds check it runs past the end of the array for a value that is not there.

Inside the loop the element is compared with the search value; if they are equal the flag is set to TRUE, otherwise the index moves on by one. When the loop ends, the flag is tested to decide what to output. If the flag is TRUE the value was found at the current index; if it is FALSE the value is not in the array and the index has ended one beyond the upper bound, which is why the flag rather than the index decides what is reported.

A linear search is easy to write and needs no ordering, but it is slow on large arrays. A successful search takes between 1 comparison, if the value happens to be first, and n comparisons, if it is last; an unsuccessful search always takes n comparisons because every element must be checked. Doubling the amount of data doubles the work, so for a large amount of sorted data an algorithm that uses the order to discard half the data at a time is dramatically faster.

Line of pseudocodeWhy it is there
Found <- FALSEInitialises the flag before the loop; without it the loop condition is undefined
Index <- 1Starts at the lower bound of the array
WHILE NOT Found AND Index <= 500 DOTwo conditions: stop when found, and stop at the end of the array
IF ProductCode[Index] = SearchCode THENThe comparison that does the work
Found <- TRUERecords the find, which ends the loop on the next test
ELSEThe two outcomes of the comparison, kept separate
Index <- Index + 1Moves to the next element; if this is missed the loop never ends
ENDIFCloses the IF
ENDWHILECloses the loop
IF Found THEN OUTPUT Index ELSE OUTPUT "Not found"Reports the result after the loop, testing the flag
Write the loop as 'not found AND index within bounds'. An answer whose loop condition is only 'NOT Found' will crash or hang, and it loses the marks for the loop even if everything else is right.

Sorting an array with a bubble sort

A bubble sort steps through the array comparing each element with the one after it, and swaps the pair whenever they are in the wrong order. On each pass the largest remaining value moves steadily towards the end of the array and stays there, which is where the name comes from. Sorting into descending order needs no change to the algorithm beyond reversing the comparison.

After the first pass the largest value is in place, after the second the second largest is in place, so n elements need at most n-1 passes. In practice the sort usually finishes sooner: if a complete pass makes no swaps then nothing was out of order, so the array must already be sorted. A Boolean flag set to FALSE at the start of every pass and to TRUE whenever a swap happens lets a REPEAT loop stop as soon as a pass is clean, which makes the algorithm much quicker on data that is nearly in order already.

Swapping two elements needs three statements and a temporary variable: copy the first value into the temporary, copy the second value into the first element, then copy the temporary into the second element. The classic mistake is two assignments without the temporary, which ends with both elements holding the same value and one value lost for ever.

A bubble sort is short, needs only one extra variable, and is simple to check by hand, but it is inefficient. With n elements the first pass makes n-1 comparisons, the next n-2, and so on, giving roughly n(n-1)/2 comparisons in the worst case, so the work grows with the square of the number of items and a large array becomes very slow. It is a sensible choice for a few dozen items, or for data that is almost sorted, and a poor choice for a file of thousands of records.

Line of pseudocodePurpose
REPEATOuter loop: one repetition is one pass through the array
Swap <- FALSEAssumes the array is sorted until a swap proves otherwise
FOR Index <- 1 TO NumberInArray - 1Inner loop compares adjacent pairs; the last index must be one less than the upper bound
IF Names[Index] > Names[Index + 1] THENThe comparison; change > to < for descending order
Temp <- Names[Index]First statement of the three-statement swap
Names[Index] <- Names[Index + 1]Second statement of the swap
Names[Index + 1] <- TempThird statement, which completes the swap without losing a value
Swap <- TRUERecords that a swap happened, so another pass is needed
ENDIFCloses the IF
NEXTMoves to the next adjacent pair
UNTIL Swap = FALSEStops after a pass in which nothing moved
The inner loop must stop at upper bound minus one, because it reads Index + 1. A loop that runs to the upper bound reads past the end of the array on its last pass.

Files: why they are needed and how they are handled

Why files are needed. Variables and arrays live in main memory, and main memory is volatile: its contents are lost when the program ends, when the computer is switched off or when the power fails. A file is held on secondary storage, so the data survives between runs, can be read back by the same program next time or by a different program, and can be copied, transferred to another computer, backed up or sent to a printer. Files also hold collections of data far too large to fit in memory at once, which can be processed one line at a time.

A text file is a sequence of characters divided into lines, and the program reads it one line at a time from the beginning to the end. Each line normally holds one record or one field, so the structure of the file has to match the structure of the program that reads it. The program has no way of knowing how many lines there are except by reaching the end, so it tests EOF, end of file, before every read; reading when the end has been reached is an error.

A file is opened before it is used and closed afterwards. OPENFILE with FOR READ opens an existing file for reading, FOR WRITE creates a new file or empties an existing one, and FOR APPEND adds new data to the end of an existing file without destroying what is already there.

READFILE reads one line into a variable, WRITEFILE writes one line, and CLOSEFILE writes any buffered data to the disk and releases the file. A program that forgets to close a file can lose the last few records it wrote.

Reading a file therefore follows a fixed pattern: open it, test EOF, read one line, process that line, and repeat until EOF is true, then close it. If the lines are records, each line is read into a record or split into fields; if the program needs to keep all of the data, each line is copied into the next element of an array as it is read, which is how a file of records becomes an array of records in memory.

StatementWhat it doesPoint to remember
OPENFILE "pupils.txt" FOR READOpens an existing file so that lines can be readThe file must exist, or the open fails
OPENFILE "pupils.txt" FOR WRITECreates the file, or empties an existing one, ready to writeAny previous contents are lost, so WRITE is never used to add to a file
OPENFILE "pupils.txt" FOR APPENDOpens the file ready to add after the last lineThe existing lines are kept
READFILE "pupils.txt", LineOfTextReads the next line into the variableOnly ever used after testing EOF
WRITEFILE "pupils.txt", Pupil.LastNameWrites one line to the fileEach WRITEFILE starts a new line
EOF("pupils.txt")TRUE when the whole file has been readTest it before every read, including the first
CLOSEFILE "pupils.txt"Finishes with the file and flushes it to diskEvery file that is opened must be closed
A file question is marked on the order of the statements: open, loop testing EOF, read, process, close. An answer that reads first and tests EOF afterwards loses the mark for the loop, because it falls off the end of the file.

Stacks and queues as abstract data types

An abstract data type (ADT) is a collection of data together with the set of operations that can be performed on that data. It is abstract because the user of the ADT knows what each operation does but not how the data is stored or how the operations are implemented. A stack can be built from an array or from a linked list, and the program that uses the stack does not need to know which, because the operations behave the same way in both cases.

This is the same idea as abstraction elsewhere in the course: keep the essential behaviour and hide the detail. A stack is a last in, first out (LIFO) structure. Data is added with push and removed with pop, and both operations work at one end only, the top; a peek (or top) operation reads the top value without removing it.

Because only the top is accessible, the item that was pushed most recently is the first one out. Pushing onto a full stack causes overflow, and popping from an empty stack causes underflow, so a well-written implementation tests for both.

A queue is a first in, first out (FIFO) structure. Data is added at one end, the rear, with enqueue, and removed from the other end, the front, with dequeue, so items leave in exactly the order they arrived. A queue is the right structure whenever fairness or arrival order matters: a print spooler, a keyboard buffer, a queue of customers, or a set of processes waiting for the processor are all queues.

Choosing between them is a question about which item must be handled next: the most recent item first means a stack, and the oldest item first means a queue. Say which operation is used and why that order gives the behaviour the situation needs, because a description of the structure without the justification does not answer a justify question.

SituationStructureWhy
Undo in a word processorStackThe most recent edit must be undone first, so LIFO order is exactly right
Back button in a browserStackEach page visited is pushed; going back pops the most recent one
Checking that brackets in an expression are balancedStackEach closing bracket must match the most recently opened one
Print spooler in an operating systemQueueJobs should print in the order they were sent, so FIFO keeps it fair
Characters typed at a keyboard while the processor is busyQueueKeystrokes must be delivered in the order they were typed or the text is scrambled
Calls waiting to be answered in a call centreQueueThe caller who has waited longest is answered next
Never write 'a stack is better'. The mark is for the order the situation needs: LIFO for the most recent first, FIFO for the longest waiting first.

Linked lists, and implementing all three with arrays

A linked list stores each item in a node. A node holds the data and a pointer to the next node, the last node's pointer holds a null value to show the list has ended, and a separate start (or head) pointer gives the position of the first node. The nodes need not be next to one another in memory, which is what makes a linked list different from an array: the list can grow and shrink while the program runs, and no maximum size has to be declared in advance.

To traverse a linked list the program starts at the start pointer and follows the pointers node by node until it reaches null; the nth item cannot be reached directly, so a linked list is slower than an array for reading a particular item but much faster for inserting and deleting.

To insert a node, the new node's pointer is set to the node it will come before and the previous node's pointer is set to the new node. To delete a node, the previous node's pointer is set to skip over it. Neither operation moves any of the other items, whereas inserting into the middle of an array means shifting every later element up one place.

All three ADTs can be implemented using arrays, which is how they are usually examined. A stack uses a 1D array and a single integer, Top, which holds the index of the current top element and is 0 when the stack is empty: push tests that Top is below the upper bound, then increments Top and stores the value; pop tests that Top is above 0, reads the value and decrements Top. Top equal to the upper bound means the stack is full, and Top equal to 0 means it is empty.

A queue uses a 1D array with two integers, Front and Rear, which mark the two ends; enqueue increments Rear and stores, dequeue reads the element at Front and increments Front. In a simple array implementation the space at the front is never reused, so the queue creeps towards the end of the array and reports itself full while most of the array is empty; a circular queue solves this by making Rear wrap round to the start with MOD, reusing the vacated space. A linked list uses two parallel arrays, one holding the data and one holding the index of the next node, plus a start pointer and a list of free positions, so that inserting and deleting only ever changes the integer pointers.

StructureStored asWhat marks the endsWhat an operation involves
Stack1D array plus an integer TopTop is 0 when empty, upper bound when fullPush: Top <- Top + 1, then store. Pop: read, then Top <- Top - 1
Queue1D array plus Front and RearFront = Rear means empty in a simple implementationEnqueue: Rear <- Rear + 1, then store. Dequeue: read at Front, then Front <- Front + 1
Circular queue1D array plus Front, Rear and a count or a full flagRear wraps round with MOD so the free space at the front is reusedEnqueue: Rear <- (Rear + 1) MOD Size, then store
Linked listParallel arrays Data and Next, plus Start and a free listNext holding a null value marks the last nodeInsert: set the new node's Next, then the previous node's Next. Delete: point the previous node past the deleted node
You are not asked to write pseudocode for these structures, but you are asked to add, edit and delete data from them, so be ready to describe the pointer changes in words, step by step.

Key terms you must be able to define

Data type
The kind of value a variable, field or array element can hold, which also fixes the operations allowed on it. Cambridge pseudocode uses INTEGER, REAL, CHAR, STRING, BOOLEAN and DATE, with ARRAY and FILE as structured types.
Record
A structure that holds a set of data of different data types under one identifier. The type is defined once with TYPE ... ENDTYPE, and each field is reached using the record identifier, a dot and the field name.
Field
One named component of a record, with its own identifier and data type, for example LastName : STRING or DateOfBirth : DATE inside a pupil record.
Index (subscript)
The value written in brackets after an array identifier that selects one element, for example the 4 in Scores[4]. It may be a constant, a variable or an expression.
Lower bound
The smallest index an array allows. In ARRAY[1:30] the lower bound is 1; in ARRAY[0:9] it is 0.
Upper bound
The largest index an array allows. In ARRAY[1:30] the upper bound is 30, and the array holds upper bound minus lower bound plus one, which is 30, elements.
2D array
An array whose elements are selected by two indices, the first for the row and the second for the column, used for data laid out in a grid such as a seating plan or a table of marks.
Linear search
A search that examines each element of an array in turn from the lower bound until the value is found or the end of the array is reached. It works on unsorted data and takes at most n comparisons for n elements.
Bubble sort
A sort that repeatedly compares adjacent elements and swaps those in the wrong order, so that on each pass the next largest value moves to its final position. It stops early when a complete pass makes no swaps.
Text file
A file held on secondary storage as a sequence of characters divided into lines, read one line at a time from the beginning to the end, with EOF tested before every read.
Abstract data type (ADT)
A collection of data together with the set of operations that can be performed on that data, where the user of the ADT knows what the operations do but not how the data or the operations are implemented.
Stack
A last in, first out (LIFO) ADT in which data is added with push and removed with pop at one end only, the top. Pushing onto a full stack causes overflow; popping an empty stack causes underflow.
Queue
A first in, first out (FIFO) ADT in which data is added at the rear with enqueue and removed from the front with dequeue, so items leave in the order in which they arrived.
Linked list
An ADT in which each item is held in a node containing the data and a pointer to the next node, with a start pointer for the first node. Items can be inserted and deleted by changing pointers, without moving the other items, and the list can grow at run time.

Applied to real systems

9618 sets its questions inside systems you already use - a phone, a school network, a supermarket till, a website, a bank. These show how to apply the theory to a context you have not seen before, then you write your own.

Undo in a word processor: a stack

Every edit a user makes is pushed onto an undo stack, and Ctrl+Z pops the most recent one off. The behaviour is exactly LIFO: after typing a sentence, changing a font and deleting a word, the first undo reverses the deletion, because that was the last thing done. A queue would be useless here, because it would reverse the oldest edit first and leave the most recent mistakes in place. The same structure, with a limit on its depth, explains why a program only offers a certain number of undo steps.

The print spooler in an operating system: a queue

When several documents are sent to a printer they are placed in a spool queue and printed in the order they arrived, so a small one-page job sent first is not overtaken by a 200-page report sent two seconds later. The operating system enqueues each job at the rear and dequeues from the front, and it needs the queue because the processor can accept jobs far faster than the printer can print them. The queue is also the buffer that lets each program finish and close while its output is still waiting.

A music player playlist: a linked list

A playlist of tracks behaves like a linked list: each track is a node, and the player moves to the next track by following the pointer rather than by calculating a position in an array. Adding a track in the middle, or removing one, only changes two pointers, so the rest of the list does not move and the playlist can be any length. This is why a playlist can be edited while it is playing, which an array with a declared upper bound of, say, 100 tracks could not do gracefully.

A school's pupil records: records and a text file

A school stores one line per pupil in a text file, with the fields in a fixed order (surname, forename, date of birth, year group, fees paid), and the program declares a record type with a field for each. The program opens the file for reading, tests EOF, reads a line, splits it into the fields of a record, copies the record into the next element of an array of records, and closes the file. Saving the data works the other way round, and using APPEND rather than WRITE means a newly admitted pupil can be added without rewriting the whole file.

A cinema seat booking system: a 2D array

A screen with 12 rows of 20 seats is held as ARRAY[1:12, 1:20] OF BOOLEAN, where TRUE means the seat is booked. Booking a seat checks the element and sets it, and displaying the screen is a pair of nested loops that prints a row of 20 symbols for each of the 12 rows. The two indices are exactly the two facts that identify a seat, which is the test for choosing a 2D array over a 1D one.

Flashcards

Click a card to flip it. Mark honestly — the tracker uses this.

Q1Name the six simple data types in Cambridge pseudocode and say what each holds.
INTEGER: whole numbers, positive, negative or zero. REAL: a number with a fractional part. CHAR: exactly one character. STRING: zero or more characters. BOOLEAN: TRUE or FALSE only. DATE: a calendar date, which can be compared and used in calculations such as finding a number of days.
Q2Which data type would you choose for a postcode or a telephone number, and why not an INTEGER?
STRING. A postcode contains letters and spaces, a telephone number may begin with 0, and neither is used in arithmetic, so an INTEGER would lose the leading zero and could not hold the letters and spaces.
Q3What is a record, and how is one defined and used in pseudocode?
A structure holding a set of data of different data types under one identifier. Define the type with TYPE Name ... ENDTYPE, declaring each field with its own type inside it. Then declare a variable of that type, for example DECLARE Pupil : StudentRecord, and reach a field with dot notation, for example Pupil.LastName <- "Okafor" or INPUT Pupil.YearGroup.
Q4Define index, lower bound and upper bound, and give the number of elements in ARRAY[1:30].
The index (subscript) is the value in brackets that selects one element. The lower bound is the smallest index allowed, the upper bound the largest. The number of elements is upper minus lower plus one, so ARRAY[1:30] holds 30 elements and ARRAY[0:9] holds 10.
Q5When would you choose a 2D array rather than a 1D array?
When each item is identified by two facts, such as a seat by row and number, a mark by pupil and subject, or a pixel by x and y. A list of names needs only one index, so a 1D array is enough. The first index is the row and the second is the column, and that order must stay the same throughout the program.
Q6Write the loop condition of a linear search and explain both halves.
WHILE NOT Found AND Index <= UpperBound DO. NOT Found stops the loop as soon as the value is found; Index <= UpperBound stops it running past the end of the array when the value is not there. The index and the flag are both initialised before the loop, and the flag, not the index, decides what is output afterwards.
Q7How many comparisons does a linear search take, and does the data need to be sorted?
At most n comparisons for n elements, and exactly n for an unsuccessful search; the best case is 1. The data does not need to be sorted, which is the advantage of a linear search. Doubling the amount of data doubles the work, so it is slow on large arrays.
Q8How does one pass of a bubble sort change the array, and how does the sort stop early?
A pass compares each adjacent pair and swaps the pair if they are in the wrong order, so the largest remaining value moves to the end and stays there; after at most n-1 passes the array is sorted. A swap uses three assignments through a temporary variable. If a whole pass makes no swaps, the Swap flag stays FALSE and REPEAT ... UNTIL Swap = FALSE ends the sort early.
Q9Give four reasons why files are needed, and write the pattern for reading every line.
Main memory is volatile, so data in variables and arrays is lost when the program ends; a file on secondary storage keeps it. A file can hold more data than fits in memory, can be read by another program or on another computer, and can be backed up, archived or printed. Pattern: OPENFILE "data.txt" FOR READ, WHILE NOT EOF("data.txt") DO, READFILE "data.txt", LineOfText, process the line, ENDWHILE, CLOSEFILE "data.txt".
Q10Define an abstract data type and name the three in this syllabus.
A collection of data together with the set of operations that can be performed on that data, where the user of the ADT knows what the operations do but not how the data or the operations are implemented. The three are the stack, the queue and the linked list.
Q11Contrast a stack and a queue: operations, order and one use of each.
Stack: last in, first out, with push and pop at one end only, the top, so the most recent item is removed first; used for undo and the browser back button, and pushing onto a full stack is overflow. Queue: first in, first out, with enqueue at the rear and dequeue at the front, so the item that has waited longest is removed first; used for a print spooler or a keyboard buffer.
Q12What does a linked list node contain, why use a linked list rather than an array, and how are a stack and a queue implemented using arrays?
A node holds the data and a pointer to the next node; the last pointer is null and a start pointer gives the first node. A linked list can grow and shrink at run time, and inserting or deleting an item only changes pointers, so no other items move. Stack: 1D array plus Top, which is 0 when empty; push increments Top and stores, pop reads and decrements. Queue: 1D array plus Front and Rear; enqueue increments Rear and stores, dequeue reads at Front and increments it, and the space at the front is only reused if the queue is made circular.
0 of 12 marked as known

Exam-style questions

8 questions, 26 marks. Write your answer, then reveal the mark scheme and tick each point you actually made. Answering honestly here is what makes the rest of the site useful.

StateAO11 mark
A library program stores the date on which a book is borrowed. State the most appropriate data type for this value.

Mark scheme — tick each point you actually made

Examiner tip: One mark for one word. STRING is a common wrong answer, but a date that is only ever displayed is not what is being stored here, and the examiners want the type that supports comparison and arithmetic.
DefineAO12 marks
Define the term abstract data type (ADT).

Mark scheme — tick each point you actually made

Examiner tip: Two halves: the data plus the operations, and the hiding of the implementation. 'A stack is an ADT' on its own earns nothing, because it defines nothing.
ExplainAO23 marks
A program stores the details of 300 students, including each student's name, date of birth, year group and whether the fees have been paid. Explain why a record structure is more suitable than four separate arrays for holding this data.

Mark scheme — tick each point you actually made

Examiner tip: The marks are for the grouping, the handling as one item, and the risk that parallel arrays fall out of step. A description of what a record is, without saying what it makes possible, is a definition answer to an explain question.
WriteAO24 marks
The 1D array ProductCode has a lower bound of 1 and an upper bound of 500 and holds product codes in no particular order. A code is entered by the user. Write pseudocode for a linear search that outputs the position of the code in the array, or a message if the code is not present.

Mark scheme — tick each point you actually made

Examiner tip: The fourth mark is the one most often lost: an answer that outputs Index without testing the flag reports position 501 for a code that is not in the array. The third mark needs the increment to be inside the loop, otherwise the search never moves.
WriteAO24 marks
The 1D array Names contains 100 names, held in elements 1 to 100. Write pseudocode to sort the names into ascending order using a bubble sort.

Mark scheme — tick each point you actually made

Examiner tip: The swap must be three statements through a temporary variable; two assignments lose a name and forfeit the mark. Check your inner loop bound: if it reads Index + 1, the loop must stop at 99, not 100.
ExplainAO23 marks
A program calculates the marks of 500 students and holds them in an array. Explain why the program should also save the marks to a file.

Mark scheme — tick each point you actually made

Examiner tip: Say 'volatile' or 'lost when the program ends', not 'so the data is safe'. The second and third marks need the idea of sharing or of size, which are separate reasons from persistence.
JustifyAO23 marks
A web browser records the pages a user visits so that the Back button returns to the page visited immediately before the current one. Justify the use of a stack rather than a queue for this purpose.

Mark scheme — tick each point you actually made

Examiner tip: The justification is about the order: the third mark exists because you have to say why the other structure fails, not just why yours works.
CompareAO26 marks
Compare a stack and a queue, and describe how each can be implemented using a 1D array. In your answer you should refer to the operations used to add and remove data.

Mark scheme — tick each point you actually made

Examiner tip: Compare means both structures in every paragraph: name the operation, say which end it works on, and give the array implementation detail. Six marks for six separate statements is the safest structure.

Exam technique for this topic

  • Write pseudocode in the guide's syntax, not in English with code words mixed in: DECLARE Name : INTEGER, assignment with <-, IF ... THEN ... ELSE ... ENDIF, FOR ... NEXT, WHILE ... ENDWHILE, and indentation of two spaces for each level. Marks are awarded for the structure as well as the logic, so a correct algorithm written as a paragraph scores little.
  • For any search or sort, count the marks and make sure you have written that many separate pieces of logic: initialisation, loop condition, comparison, action, increment or swap, and the result. In a linear search answer the loop condition needs both 'not found' and 'index within bounds', and in a bubble sort answer the swap needs a temporary variable.
  • Read the question for the detail that changes the answer: the lower bound of the array, whether the data is sorted, whether the file is opened for writing or appending, and whether the value may not be present. A search that never reports 'not found' and a sort that reads past the upper bound are the two most common failures in this section.
  • When a question asks you to justify a data type, a structure or an ADT, give the reason from the situation rather than a definition: 'DATE, because the fine is worked out from the number of days between the borrow date and today' or 'a queue, because the jobs must print in the order they were sent'.
  • Questions on the ADTs are answered in words, not pseudocode: name the operations (push and pop, enqueue and dequeue, insert and delete by pointer), say which end they work on, and where a question asks about implementation, name the pointers (Top, Front and Rear, Start and the next-node array) and say what changes when data is added or removed.

Why students lose marks here

A student wroteA student wrote: 'The telephone number should be stored as an INTEGER because it is made of digits, and the price of the ticket should be stored as an INTEGER because it is a number.'
Why it loses marksBoth choices lose information. An INTEGER cannot hold a leading zero, so 07700 900123 becomes 7700900123, and it cannot hold the spaces or the plus sign in an international number. A ticket price such as 12.50 needs a fractional part, which an INTEGER cannot store, so the 50 pence is lost or the value is rejected.
The answer that scoresThe telephone number should be a STRING, because it is not used in arithmetic, it may begin with 0, and it may contain spaces, brackets or a leading + for an international number; storing it as an INTEGER would drop the leading zero and could not hold the other characters. The price should be a REAL, because it can have a fractional part and is used in arithmetic such as adding up a total or working out change. If the program must avoid the rounding errors of binary floating point, the price can instead be stored as an INTEGER number of pence and divided by 100 only when it is displayed.
A student wroteA student's bubble sort contained: 'IF Names[Index] > Names[Index + 1] THEN Names[Index] <- Names[Index + 1] Names[Index + 1] <- Names[Index] ENDIF'.
Why it loses marksThere is no temporary variable, so the first assignment destroys the value that the second assignment needs. Both elements end up holding the smaller value, one name is lost, and the array can never be sorted. The second line also needs the ENDIF to be on its own line, but the lost value is the error that costs the marks.
The answer that scoresA swap needs three assignments through a temporary variable: Temp <- Names[Index], then Names[Index] <- Names[Index + 1], then Names[Index + 1] <- Temp. The first statement saves the value that is about to be overwritten, the second moves the smaller value down, and the third puts the saved value into the upper position. The whole swap sits inside IF Names[Index] > Names[Index + 1] THEN ... ENDIF, and a Boolean flag is set to TRUE inside the same IF so that the outer REPEAT ... UNTIL Swap = FALSE loop knows another pass is needed.
A student wroteA student's linear search read: 'Index <- 1 WHILE ProductCode[Index] <> SearchCode DO Index <- Index + 1 ENDWHILE OUTPUT Index'.
Why it loses marksThe loop has no bounds check, so a code that is not in the array runs the index past the upper bound and the program fails with an index error or reads rubbish. It also has no way of reporting that the value was not found, so it outputs a position that does not exist. There is no Found flag, so the program cannot tell a successful search from a failed one.
The answer that scoresThe search must initialise both an index and a flag, and its loop condition must contain both conditions: Found <- FALSE, Index <- 1, then WHILE NOT Found AND Index <= 500 DO, comparing ProductCode[Index] with SearchCode, setting Found <- TRUE when they match and otherwise incrementing Index. After the loop, test the flag: if Found is TRUE then output Index, otherwise output a message such as "Code not found". Because the index is incremented while the value is not found, it ends at 501 for an unsuccessful search, which is exactly why the flag rather than the index decides the output.
A student wroteA student wrote: 'The program opens the file FOR WRITE each time a pupil is added so that the new pupil is stored with the others, and it reads the file with READFILE until the program crashes, which shows it has reached the end.'
Why it loses marksOpening FOR WRITE empties the file, so every pupil already stored is destroyed and the file ends up holding only the last one added. Waiting for a crash is not a way of detecting the end of a file: the program should test EOF before each read, and a crash means data has already been read from beyond the end.
The answer that scoresTo add a pupil without losing the existing records the file is opened FOR APPEND, which positions the write at the end and leaves the existing lines untouched; FOR WRITE would be used only when the whole file is being created again. To read it, the program opens FOR READ, then loops WHILE NOT EOF("pupils.txt") DO, reading one line with READFILE inside the loop and processing it, and closes the file with CLOSEFILE when the loop ends. Testing EOF before the read means the loop stops cleanly after the last line instead of reading past the end.
A student wroteA student wrote: 'A queue is last in, first out, so the most recently added item is removed first, which is why a queue is used for the back button in a browser.'
Why it loses marksA queue is first in, first out: items are added at the rear and removed from the front, so the oldest item leaves first. LIFO is the behaviour of a stack, not a queue, and the back button needs the most recent page, so the structure named is wrong as well as the definition.
The answer that scoresA stack is last in, first out: each page visited is pushed onto the top of the stack, and the back button pops the most recent page off the top, which is exactly the page the user expects to see. A queue is first in, first out: items are enqueued at the rear and dequeued from the front, so the oldest item is removed first, which suits a print spooler where jobs must print in the order they were sent. The back button therefore needs a stack, because the most recent page must be the first one removed.