How this works
Choose a question, write your solution in C or C++17, run the supplied tests, then mark it complete. Difficulty is shown by the dots: ●●●● intro, ●●●● core, ●●●● advanced, ●●●● brutal.
Edit the starter, provide input and run the supplied tests without installing anything. Compilation happens locally in your browser.
1. Pick a question
Open any question and choose Open in compiler. C is the default; use the arrow beside it to choose C++17. Your last choice is remembered in this browser.
2. Write and test
Fill in the starter’s TODO, run it with your own input, then
use Run tests to check every supplied case. When it passes,
use the question’s checkbox to remember your progress.
Work locally instead
Use the download button in the Questions panel, then unzip it anywhere. If you filter the questions first, the download includes only the questions currently shown.
If ./autotest.sh says
Permission denied, run this once:
chmod +x autotest.shDownload a question’s prac_qN.c file, save it beside
autotest.sh, and fill in the // TODO. Keep the
filename and the supplied main unchanged.
./autotest.sh 1 # check question 1
./autotest.sh 1 5 12 # check several
./autotest.sh # check every question in the folderThe script compiles your file and reports failing inputs, compile errors,
crashes and timeouts. It needs bash and a C compiler. Linux,
macOS and CSE work; on Windows, use WSL or a CSE machine.
Safety and page controls
The readable script uses no network access, installs nothing and never
needs sudo. Nothing runs until you start it. Press
Ctrl + K for page shortcuts, and use each
question’s checkbox to remember your progress in this browser.
Question 1Count Negative Numbers
Estimated time: 10-15 minutes
A finance application processes an array of transaction amounts (where negative values represent withdrawals) and wants a quick summary of how many withdrawals occurred. Write a function that counts how many elements of an array of n integers are strictly negative.
Zero should not be counted as negative, since it represents neither a deposit nor a withdrawal.
Your task is to complete the function int count_negative(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q1.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q1.c:
gcc -Wall -Wextra -o prac_q1 prac_q1.c
./prac_q1
6
-3 5 -1 0 8 -9
3
./prac_q1
3
1 2 3
0
./prac_q1
0
0Assumptions / Restrictions / Clarifications
nmay be 0, in which case the function should return 0.- Zero is not considered negative and must not be counted.
arrmay contain any mixture of negative, zero and positive values.- Do not call
scanf,getcharorfgetsinsidecount_negative; all input is provided by main viaarrandn. - Do not print anything inside
count_negative; only main should print.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- array traversal with an index loop
- comparing elements against a constant
- accumulating a running count
Worked example
For example:
For the array -3 5 -1 0 8 -9:
- -3 is negative, so it counts.
- 5 is positive, does not count.
- -1 is negative, so it counts.
- 0 is not negative (it is neither positive nor negative), does not count.
- 8 is positive, does not count.
- -9 is negative, so it counts.
There are 3 negative elements in total, so count_negative returns 3.
Edge cases to consider
nis 0 (empty array) so the loop body never runs and the answer is 0- an array containing 0s, which must not be counted
- an array where every element is negative
- an array with no negatives at all
Common mistakes
- counting 0 as negative by using
<= 0instead of< 0 - starting the counter at 1 instead of 0
- looping to
i <= nand reading one element past the end
Optional extension challenge
Also return the sum of the negative values via a second int pointer parameter, without changing the return type of the count.
You can re-fetch the starter code for this question: prac_q1.c.
Question 2Count Uppercase Letters
Estimated time: 10-15 minutes
A password-strength checker wants to verify that a candidate password contains a reasonable number of uppercase letters. Write a function that counts the number of uppercase alphabetic characters in a null-terminated string.
The function should examine every character in the string once and only count letters in the range 'A' to 'Z'.
Your task is to complete the function int count_upper(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q2.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q2.c:
gcc -Wall -Wextra -o prac_q2 prac_q2.c
./prac_q2
Hello World
2
./prac_q2
no capitals here
0
./prac_q2
SHOUTING
8Assumptions / Restrictions / Clarifications
- Non-alphabetic characters (digits, spaces, punctuation) should be ignored.
- Lowercase letters should not be counted.
- The string may be empty, in which case the function returns 0.
- Do not call
scanf,getcharorfgetsinsidecount_upper; the string is passed in as an argument. count_uppermust not modify the string it is given.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- iterating a null-terminated string
- character range comparisons (
'A'..'Z') - read-only use of a string argument
Worked example
For example:
For the string "Hello World":
- 'H' is uppercase, so it counts.
- 'ello ' are all lowercase or a space, none count.
- 'W' is uppercase, so it counts.
- 'orld' are all lowercase, none count.
There are 2 uppercase letters in total, so count_upper returns 2.
Edge cases to consider
- the empty string "", which returns 0
- a string with no uppercase letters
- a string that is ALL uppercase
- strings containing digits, spaces and punctuation that must be ignored
Common mistakes
- using the loop condition
i < strlen(s)recomputed every iteration instead ofs[i] != '\0' - accidentally counting lowercase letters by comparing against
'a'..'z' - using ctype without including
<ctype.h>, or writing the range test back-to-front
Optional extension challenge
Return the count of uppercase letters minus lowercase letters to measure the 'shoutiness' of the text.
You can re-fetch the starter code for this question: prac_q2.c.
Question 3Count Occurrences of a Character
Estimated time: 10-15 minutes
A text-analysis tool wants to know how frequently a particular character (for example a punctuation mark or a specific letter) appears within a block of text. Write a function that counts how many times a given character occurs within a null-terminated string.
The function should scan the entire string once and tally every exact match of the target character.
Your task is to complete the function int count_char(char *s, char target), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q3.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q3.c:
gcc -Wall -Wextra -o prac_q3 prac_q3.c
./prac_q3
banana
a
3
./prac_q3
banana
z
0
./prac_q3
Banana
B
1Assumptions / Restrictions / Clarifications
- Comparison is case-sensitive, so
'a'and'A'are considered different characters. - The string may be empty, in which case the function returns 0.
targetmay be any validchar, including the space character or a digit.- Do not call
scanf,getcharorfgetsinsidecount_char; both arguments are passed in directly.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- string traversal
- exact character equality
- passing a
charby value
Worked example
For example:
For the string "banana" and target 'a':
- the letters at positions 1, 3 and 5 (0-indexed) are 'a', 'a', 'a'.
- every other letter ('b', 'n', 'n') is not 'a'.
There are 3 occurrences of 'a', so count_char returns 3.
Edge cases to consider
- the empty string, returning 0
targetthat does not appear at all- counting the space character or a digit as the
target - a
targetthat appears at the very first and very last position
Common mistakes
- doing a case-insensitive compare when the spec requires case-sensitive
- comparing
s[i]to the wholetargetstring instead of a singlechar - stopping at the first match instead of counting all matches
Optional extension challenge
Make the comparison case-insensitive by adding an int flag parameter, treating 'a' and 'A' as equal only when the flag is set.
You can re-fetch the starter code for this question: prac_q3.c.
Question 4String Length Without strlen
Estimated time: 10-15 minutes
As an exercise in understanding how C strings work under the hood, you are to write your own version of the standard library's strlen function, which returns the number of characters in a null-terminated string, not counting the null terminator itself.
You may not call the real strlen (or any other library function that computes string length) to implement this; you must count the characters yourself by scanning until the null terminator.
Your task is to complete the function int my_strlen(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q4.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q4.c:
gcc -Wall -Wextra -o prac_q4 prac_q4.c
./prac_q4
hello
5
./prac_q4
0
./prac_q4
a b c
5Assumptions / Restrictions / Clarifications
- The string may be empty, in which case the function returns 0.
sis always a valid, non-NULL, null-terminated string.- The count does not include the null terminator character itself.
- You must not call
strlen(or any equivalent library function) insidemy_strlen.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- understanding the null terminator
- manual string traversal
- returning a computed length
Worked example
For example:
For the string "a b c":
- the characters are 'a', ' ', 'b', ' ', 'c' -- five characters in total, including the spaces.
- the scan stops as soon as the null terminator
'\0'after 'c' is reached.
my_strlen therefore returns 5.
Edge cases to consider
- the empty string, whose length is 0
- a single-character string
- a string containing spaces (which are part of the length)
Common mistakes
- counting the
'\0'itself in the length - returning the loop index off by one
- assuming a fixed buffer size instead of stopping at
'\0'
Optional extension challenge
Write a companion that returns the length up to the first space, or the full length if there is no space.
You can re-fetch the starter code for this question: prac_q4.c.
Question 5Swap Two Integers via Pointers
Estimated time: 10-15 minutes
As part of learning how pointers let a function modify variables in the caller's scope, you are to write a small utility that exchanges the values of two integer variables. Write a function that swaps the values pointed to by two int pointers.
Because C passes arguments by value, the function must take pointers to the two integers so that it can modify the caller's original variables rather than local copies.
Your task is to complete the function void swap_ints(int *a, int *b), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q5.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q5.c:
gcc -Wall -Wextra -o prac_q5 prac_q5.c
./prac_q5
3 8
8 3
./prac_q5
-1 -9
-9 -1
./prac_q5
5 5
5 5Assumptions / Restrictions / Clarifications
aandbare always valid, non-NULLpointers toint.aandbmay point to the same address, in which case the values should be unchanged after the call.- The values pointed to may be negative, zero, or positive.
swap_intsmust not return a value; it communicates entirely through the pointers it is given.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- pointer dereferencing
- modifying caller variables through pointers
- using a temporary variable
Worked example
For example:
For x = 3 and y = 8:
- before the call,
xholds 3 andyholds 8. swap_ints(&x, &y)copies*a(3) into a temporary, copies*b(8) into*a, then copies the temporary (3) into*b.- after the call,
xholds 8 andyholds 3.
main prints "8 3".
Edge cases to consider
- swapping a value with itself (both pointers refer to equal values)
- negative values
- the two values already being in the desired order
Common mistakes
- swapping the pointers locally instead of the values they point to
- losing a value by assigning before saving it in a temporary
- forgetting to dereference (
*avsa)
Optional extension challenge
Write a three-way rotate function rotate3(int *a, int *b, int *c) that moves a->b->c->a using only pointer dereferences.
You can re-fetch the starter code for this question: prac_q5.c.
Question 6Largest of Three
Estimated time: 10-15 minutes
A simple scoring system needs to determine the winning score out of three competitors in a round. Write a function that takes three integers and returns the largest of them.
Your function should handle any combination of positive, negative and equal values correctly.
Your task is to complete the function int largest_of_three(int a, int b, int c), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q6.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q6.c:
gcc -Wall -Wextra -o prac_q6 prac_q6.c
./prac_q6
3 7 5
7
./prac_q6
-1 -1 -2
-1
./prac_q6
4 4 4
4Assumptions / Restrictions / Clarifications
a,bandcmay be any validintvalue, including negative numbers.- If two or more values are tied for largest, return that value (ties do not matter).
- You should not use any library sorting or max functions; a few comparisons are enough.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- boolean/relational logic
- chained comparisons or nested if
- returning the correct branch value
Worked example
For example:
For a = 3, b = 7, c = 5:
- largest starts as
a = 3. b = 7is greater than 3, so largest becomes 7.c = 5is not greater than 7, so largest stays 7.
largest_of_three returns 7.
Edge cases to consider
- all three values equal
- two of the three tied for the maximum
- negative values only
- the maximum being the first, middle, or last argument
Common mistakes
- using
&&/||logic that misses the case where two values tie - returning a comparison result (0 or 1) instead of the actual largest value
- forgetting one of the three orderings
Optional extension challenge
Generalise to return the largest of an int array of length n instead of exactly three fixed arguments.
You can re-fetch the starter code for this question: prac_q6.c.
Question 7Sum of Digits
Estimated time: 10-15 minutes
A checksum utility needs to compute a simple digit sum of a number as part of validating identifiers. Write a function that returns the sum of the decimal digits of a non-negative integer. For example the digits of 4325 are 4, 3, 2 and 5, which sum to 14.
Your function should work using arithmetic (division and modulo) rather than converting the number to a string.
Your task is to complete the function int sum_of_digits(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q7.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q7.c:
gcc -Wall -Wextra -o prac_q7 prac_q7.c
./prac_q7
4325
14
./prac_q7
7
7
./prac_q7
0
0
./prac_q7
1000
1Assumptions / Restrictions / Clarifications
nis always non-negative.sum_of_digits(0)should return 0.nmay have any number of digits that fits within a normalint.- You should not use
sprintfor any string conversion to solve this.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- integer division and modulo
- loop until a value becomes 0
- digit extraction with
% 10and/ 10
Worked example
For example:
For n = 4325:
4325 % 10 = 5, sum becomes 5,nbecomes 432.432 % 10 = 2, sum becomes 7,nbecomes 43.43 % 10 = 3, sum becomes 10,nbecomes 4.4 % 10 = 4, sum becomes 14,nbecomes 0, loop stops.
sum_of_digits returns 14.
Edge cases to consider
- the number 0, whose digit sum is 0
- a single-digit number
- a number ending in one or more zeros
Common mistakes
- infinite loop from forgetting the
/ 10step - using
% 10but never dividing, so only the last digit is summed
Optional extension challenge
Return the repeated digital root (keep summing digits until a single digit remains) instead of a single pass.
You can re-fetch the starter code for this question: prac_q7.c.
Question 8Array Contains Value
Estimated time: 10-15 minutes
A ticket booking system keeps a list of already-booked seat numbers in an array and needs a quick way to check whether a particular seat number has already been taken. Write a function that returns 1 if a given target value appears anywhere in an array of n integers, and 0 otherwise.
The array is not assumed to be sorted, so the search should check elements in order until either a match is found or the whole array has been examined.
Your task is to complete the function int array_contains(int arr[], int n, int target), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q8.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q8.c:
gcc -Wall -Wextra -o prac_q8 prac_q8.c
./prac_q8
6
4 8 15 16 23 42
15
1
./prac_q8
6
4 8 15 16 23 42
7
0
./prac_q8
0
5
0Assumptions / Restrictions / Clarifications
nmay be 0, in which case the function should return 0.arrmay contain duplicate values.targetmay or may not appear inarr, and may be any validintvalue.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- linear search
- early return on a match
- returning a 0/1 truth value
Worked example
For example:
For arr = [4, 8, 15, 16, 23, 42] and target = 15:
- 4, 8 are checked and do not match.
- 15 is checked and matches
target.
The search stops as soon as the match is found, so array_contains returns 1.
Edge cases to consider
- empty array (
n == 0), always returns 0 targetat the first positiontargetat the last positiontargetappearing more than once
Common mistakes
- returning 1 unconditionally after the loop
- not returning early and overwriting a found result with a later miss
- off-by-one reading past the end of the array
Optional extension challenge
Return the index of the first match (or -1 if absent) instead of a boolean, without scanning the array twice.
You can re-fetch the starter code for this question: prac_q8.c.
Question 9Temperature Converter
Estimated time: 10-15 minutes
Weather stations around the country record temperatures in degrees Celsius, but an international reporting system your team is building needs everything converted to degrees Fahrenheit before it is displayed. Write a function that converts a temperature given in degrees Celsius into degrees Fahrenheit. The formula for the conversion is F = C * 9 / 5 + 32.
Your function should work correctly for any valid temperature, including negative values (below freezing) and fractional values (e.g. 36.6 degrees for a body temperature reading).
Your task is to complete the function double celsius_to_fahrenheit(double celsius), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q9.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q9.c:
gcc -Wall -Wextra -o prac_q9 prac_q9.c
./prac_q9
0.0
32
./prac_q9
100.0
212
./prac_q9
-40.0
-40
./prac_q9
36.6
97.88Assumptions / Restrictions / Clarifications
- You may assume
celsiusis a valid finitedouble. - Do not round the result; return the exact computed value.
celsiusmay be negative, zero, or positive, and may be a fractional value.- You do not need to validate that
celsiusis within any particular physical range.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- floating-point arithmetic
- operator precedence in a formula
- returning a
double
Worked example
For example:
For celsius = 36.6:
F = 36.6 * 9 / 5 + 32.36.6 * 9 = 329.4, and329.4 / 5 = 65.88.65.88 + 32 = 97.88.
celsius_to_fahrenheit returns 97.88.
Edge cases to consider
- 0 degrees Celsius
- negative Celsius temperatures
- a value that produces a non-integer Fahrenheit result
- the freezing/boiling reference points (0 and 100)
Common mistakes
- integer division truncating
9 / 5to 1 before multiplying - getting the
+32offset in the wrong place - declaring the result as
intand losing the fractional part
Optional extension challenge
Add the reverse conversion (Fahrenheit to Celsius) and a Celsius-to-Kelvin conversion as extra functions.
You can re-fetch the starter code for this question: prac_q9.c.
Question 10Digit Count
Estimated time: 10-15 minutes
A form-validation routine needs to check that a numeric ID entered by a user has exactly the expected number of digits. Write a function that returns the number of decimal digits in a non-negative integer.
The number 0 has 1 digit (not 0 digits), since it is still written using a single digit character.
Your task is to complete the function int digit_count(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q10.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q10.c:
gcc -Wall -Wextra -o prac_q10 prac_q10.c
./prac_q10
4325
4
./prac_q10
0
1
./prac_q10
7
1Assumptions / Restrictions / Clarifications
nis non-negative.digit_count(0)should return 1, not 0.nmay have any number of digits that fits within a normalint.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- loop-until-zero pattern
- integer division by 10
- handling a boundary value
Worked example
For example:
For n = 0:
- the special case
n == 0is detected immediately.
digit_count returns 1, since "0" is written with a single digit character, even though the value itself is zero.
Edge cases to consider
- the number 0, which has exactly 1 digit
- a single-digit number
- a large number near the
intmaximum
Common mistakes
- returning 0 for the input 0 because the while loop body never runs
- off-by-one in the count when the loop terminates
Optional extension challenge
Count digits in an arbitrary base b (2..16) passed as a parameter rather than always base 10.
You can re-fetch the starter code for this question: prac_q10.c.
Question 11Array Average
Estimated time: 10-15 minutes
A teacher has recorded a class's quiz scores in an array of integers and wants to display the average score to two or three decimal places. Write a function that computes the average (mean) of an array of n integers, as a double.
Because the average of integers is often not itself a whole number, the function must return a double rather than truncating to an int.
Your task is to complete the function double array_average(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q11.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q11.c:
gcc -Wall -Wextra -o prac_q11 prac_q11.c
./prac_q11
4
1 2 3 4
2.5
./prac_q11
1
7
7
./prac_q11
4
-2 4 -6 8
1Assumptions / Restrictions / Clarifications
- You may assume
nis greater than 0. arrmay contain negative values, zero, and positive values in any combination.- The result should be computed using floating-point division, not integer division.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- summing an array
- integer-to-double conversion
- floating-point division
Worked example
For example:
For arr = [1, 2, 3, 4]:
- the sum of the elements is 1 + 2 + 3 + 4 = 10.
- dividing by
n = 4using floating-point division gives 2.5.
array_average returns 2.5.
Edge cases to consider
- a single-element array
- an array whose sum does not divide evenly
- negative values pulling the average below zero
Common mistakes
- integer division discarding the fractional part of the average
- overflowing the running sum for very large arrays
Optional extension challenge
Return a trimmed average that ignores the single smallest and single largest values (when n is large enough).
You can re-fetch the starter code for this question: prac_q11.c.
Question 12Is Even Array
Estimated time: 10-15 minutes
A quality-control check on a batch of measurements needs to confirm that every reading in an array is an even number before proceeding with further processing. Write a function that returns 1 if every element of an array of n integers is even, and 0 otherwise.
Your function should be able to stop early as soon as it finds an odd element, since at that point the answer is already known.
Your task is to complete the function int all_even(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q12.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q12.c:
gcc -Wall -Wextra -o prac_q12 prac_q12.c
./prac_q12
4
2 4 6 8
1
./prac_q12
3
2 3 6
0
./prac_q12
0
1Assumptions / Restrictions / Clarifications
- If
nis 0, the function should return 1 (vacuously true, since there is no counter-example). arrmay contain negative numbers; negative even numbers such as -4 still count as even.arrmay contain zero, which is considered even.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- array traversal
- modulo to test parity
- all-must-hold (universal) checks with early exit
Worked example
For example:
For arr = [2, 3, 6]:
2 % 2 == 0, so it is even, keep scanning.3 % 2 != 0, so an odd element has been found.
all_even returns 0 immediately, without checking the remaining element 6.
Edge cases to consider
- empty array (vacuously true, returns 1)
- an array containing 0 (which is even)
- an array with a single odd value that must flip the result to 0
- negative even and odd values
Common mistakes
- returning 0/1 based only on the last element instead of all elements
- assuming negatives are never even (e.g. -4 is even)
- returning early with 1 before checking the whole array
Optional extension challenge
Return the index of the first odd element (or -1 if all are even) instead of a plain boolean.
You can re-fetch the starter code for this question: prac_q12.c.
Question 13Reverse an Array In Place
Estimated time: 10-15 minutes
A playlist shuffler feature needs to be able to play a list of track indices in reverse order without using any extra memory for a second list. Write a function that reverses the order of elements in an array of n integers in place (i.e. modifying the original array, using no extra array).
After the function returns, the caller's array should contain the same values but in reverse order; no new array should be allocated or returned.
Your task is to complete the function void reverse_array(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q13.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q13.c:
gcc -Wall -Wextra -o prac_q13 prac_q13.c
./prac_q13
5
1 2 3 4 5
5 4 3 2 1
./prac_q13
4
10 20 30 40
40 30 20 10
./prac_q13
0Assumptions / Restrictions / Clarifications
nmay be 0, in which case the function should do nothing.- You must not allocate a second array to perform the reversal.
arrmay contain duplicate values.- The array should be reversed using swaps working inward from both ends.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- two-pointer / two-index technique
- swapping elements
- in-place modification without extra arrays
Worked example
For example:
For arr = [1, 2, 3, 4, 5]:
i = 0,j = 4: swaparr[0]andarr[4]-> [5, 2, 3, 4, 1].i = 1,j = 3: swaparr[1]andarr[3]-> [5, 4, 3, 2, 1].i = 2,j = 2:iis no longer less thanj, so the loop stops.
The array printed by main is "5 4 3 2 1".
Edge cases to consider
- empty array (nothing to do)
- a single-element array (already reversed)
- an even vs odd length (middle element must stay put)
Common mistakes
- looping over the whole array and swapping twice, undoing the reversal
- off-by-one when computing the mirrored index (
n - 1 - i) - allocating a second array when the task says in place
Optional extension challenge
Reverse only a sub-range [lo, hi] of the array given two extra index parameters.
You can re-fetch the starter code for this question: prac_q13.c.
Question 14Find Minimum in Array
Estimated time: 10-15 minutes
A weather logging system stores a day's temperature readings in an array and needs to quickly identify the coldest reading of the day. Write a function that returns the smallest value in an array of n integers.
The array is not guaranteed to be sorted, so your function must examine every element to be sure it has found the true minimum.
Your task is to complete the function int find_min(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q14.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q14.c:
gcc -Wall -Wextra -o prac_q14 prac_q14.c
./prac_q14
5
5 3 8 1 9
1
./prac_q14
3
-4 -10 -2
-10
./prac_q14
1
42
42Assumptions / Restrictions / Clarifications
- You may assume
nis greater than 0; the function is never called on an empty array. arrmay contain negative numbers, zero, positive numbers, or duplicate values.- You should not modify the contents of
arr.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- tracking a running best value
- initialising from the first element
- linear scan
Worked example
For example:
For arr = [-4, -10, -2]:
minstarts asarr[0] = -4.-10 < -4, sominbecomes -10.- -2 is not less than -10,
minstays -10.
find_min returns -10.
Edge cases to consider
- a single-element array
- all elements equal
- the minimum being the first or the last element
- arrays containing negative values
Common mistakes
- initialising the minimum to 0 (wrong when all values are positive or all negative)
- starting the scan before there is a valid first element to compare
- returning the index instead of the value (or vice versa)
Optional extension challenge
Return both the minimum and maximum in a single pass using two output pointer parameters.
You can re-fetch the starter code for this question: prac_q14.c.
Question 15Factorial (Iterative)
Estimated time: 10-15 minutes
A combinatorics teaching tool needs to compute factorials to demonstrate permutation counts to students. Write a function that computes n! for a non-negative integer n using iteration (a loop), returning the result as a long long.
Recall that n! is the product of all positive integers from 1 up to n, and that 0! is defined to be 1.
Your task is to complete the function long long factorial(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q15.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q15.c:
gcc -Wall -Wextra -o prac_q15 prac_q15.c
./prac_q15
5
120
./prac_q15
0
1
./prac_q15
1
1
./prac_q15
20
2432902008176640000Assumptions / Restrictions / Clarifications
- You may assume
0 <= n <= 20(so the result always fits in along long). factorial(0)should return 1, by definition.- You must use iteration (a loop); a recursive solution is not required for this problem.
- The return type is
long long, notint, to avoid overflow on both native and browser compilers.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- accumulating a product in a loop
- the empty-product base case
- integer overflow awareness
Worked example
For example:
For n = 5:
- result starts at 1.
- multiplying by 2, 3, 4, then 5 in turn:
1 * 2 = 2,2 * 3 = 6,6 * 4 = 24,24 * 5 = 120.
factorial returns 120.
Edge cases to consider
0!which must equal 11!which must equal 1- a value large enough to overflow a 32-bit
int(e.g.13!)
Common mistakes
- initialising the product to 0 so every result is 0
- starting the loop at 0 and multiplying by 0
- ignoring overflow for inputs above 12
Optional extension challenge
Rewrite it recursively, then compare which version overflows first as the input grows.
You can re-fetch the starter code for this question: prac_q15.c.
Question 16Is Prime
Estimated time: 10-15 minutes
A cryptography teaching tool needs a basic building block to test whether small integers are prime numbers before using them in further calculations. Write a function that determines whether a given integer greater than 1 is prime, returning 1 if it is prime and 0 otherwise.
Recall that a prime number is a number greater than 1 whose only positive divisors are 1 and itself.
Your task is to complete the function int is_prime(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q16.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q16.c:
gcc -Wall -Wextra -o prac_q16 prac_q16.c -lm
./prac_q16
7
1
./prac_q16
8
0
./prac_q16
2
1
./prac_q16
9973
1Assumptions / Restrictions / Clarifications
- You may assume
n > 1; the function is never called withn <= 1. - An efficient solution only needs to check divisors up to
sqrt(n), rather than all the way up ton. - 2 is the smallest prime and should be correctly identified as prime.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- loops and modulo
- early exit on finding a factor
- handling small boundary values
Worked example
For example:
For n = 8:
i = 2:2 * 2 = 4 <= 8, and8 % 2 == 0, so a divisor has been found.
is_prime returns 0 immediately, since 8 = 2 * 4 is not prime.
Edge cases to consider
- 2, the only even prime
- 3, the smallest odd prime
- a large prime that forces the full loop to run
- a square of a prime (e.g. 9 or 25)
Common mistakes
- checking divisors up to
ninstead of stopping atsqrt(n) - starting the divisor loop at 1, which always divides
- not returning 0 as soon as a factor is found
Optional extension challenge
Print all prime factors of n instead of just a boolean yes/no.
You can re-fetch the starter code for this question: prac_q16.c.
Question 17Count Vowels
Estimated time: 10-15 minutes
A simple spell-checking tool needs to estimate how vowel-heavy a piece of text is as a rough heuristic. Write a function that counts the number of vowels ('a', 'e', 'i', 'o', 'u', case insensitive) in a null-terminated string.
The count should include both lowercase and uppercase vowels, and should ignore all other characters, including consonants, digits, spaces and punctuation.
Your task is to complete the function int count_vowels(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q17.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q17.c:
gcc -Wall -Wextra -o prac_q17 prac_q17.c
./prac_q17
Hello World
3
./prac_q17
xyz
0
./prac_q17
AEIOU
5
./prac_q17
0Assumptions / Restrictions / Clarifications
- The string may be empty, in which case the function should return 0.
- Only the characters
'a','e','i','o','u'and their uppercase forms count as vowels. - The letter
'y'is never counted as a vowel. - Non-alphabetic characters (digits, spaces, punctuation) must be ignored.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- string traversal
- membership test against a small set
- case handling
Worked example
For example:
For s = "Hello World":
'e'and'o'in "Hello" are vowels (2).'o'in "World" is a vowel (1 more).- every other character ('H','l','l','W','r','l','d', the space) is not a vowel.
count_vowels returns 3.
Edge cases to consider
- the empty string
- a string with no vowels
- uppercase vowels that should also count
- the letter 'y' (decide and document whether it counts)
Common mistakes
- only checking lowercase vowels and missing uppercase ones
- a long chain of == comparisons with a mistyped vowel
- counting 'y' inconsistently
Optional extension challenge
Return counts of each individual vowel via five output parameters, not just the total.
You can re-fetch the starter code for this question: prac_q17.c.
Question 18Convert Cents to Coins
Estimated time: 10-15 minutes
A vending machine's change-making logic needs to work out the fewest coins possible to return to a customer as change. Write a function that, given a non-negative number of cents, returns the minimum number of Australian coins ($2, $1, 50c, 20c, 10c, 5c) needed to make up that amount using a greedy approach (always use the largest coin that fits).
Because Australian coin denominations happen to form a 'canonical' coin system, the greedy approach (repeatedly subtracting the largest coin that still fits) is guaranteed to produce the true minimum number of coins.
Your task is to complete the function int min_coins(int cents), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q18.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q18.c:
gcc -Wall -Wextra -o prac_q18 prac_q18.c
./prac_q18
85
4
./prac_q18
0
0
./prac_q18
500
3Assumptions / Restrictions / Clarifications
centsis non-negative.- Coin values in
centsare: 200, 100, 50, 20, 10, 5. centsis guaranteed to be exactly representable using these coin denominations (i.e. it is a multiple of 5).
Stuck? Here's a hint
What this practises
This question gives you practice with:
- greedy denomination breakdown
- integer division and modulo
- greedy accumulation
Worked example
For example:
For cents = 85:
- one 50c coin is used, leaving 35.
- one 20c coin is used, leaving 15.
- one 10c coin is used, leaving 5.
- one 5c coin is used, leaving 0.
That is 4 coins in total, so min_coins returns 4.
Edge cases to consider
- 0
cents, returning 0 - an amount that uses only the smallest coin
- an amount that is an exact multiple of the largest coin
- an amount requiring at least one of every denomination
Common mistakes
- processing denominations from smallest to largest, breaking the greedy result
- forgetting to subtract (via %) after each denomination
- off-by-one leaving leftover
centsunaccounted for
Optional extension challenge
Report the individual coin counts as well, and handle an arbitrary list of denominations passed as an array.
You can re-fetch the starter code for this question: prac_q18.c.
Question 19Is Palindrome String
Estimated time: 10-15 minutes
A word-puzzle game wants to highlight palindromic words that players enter, such as "level" or "racecar". Write a function that determines whether a given null-terminated string reads the same forwards and backwards, returning 1 if it is a palindrome and 0 otherwise.
Comparison is case-sensitive and includes all characters exactly as they appear in the string, so spaces and capitalisation matter.
Your task is to complete the function int is_palindrome(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q19.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q19.c:
gcc -Wall -Wextra -o prac_q19 prac_q19.c
./prac_q19
level
1
./prac_q19
hello
0
./prac_q19
1
./prac_q19
Level
0Assumptions / Restrictions / Clarifications
- The empty string and single-character strings are palindromes.
- Comparison is case-sensitive; no case-folding is performed.
- Every character, including spaces and punctuation, is compared exactly as given.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- two-index inward scan
- character equality
- read-only string handling
Worked example
For example:
For s = "Level":
i = 0,j = 4:s[0] = 'L',s[4] = 'l'-- these differ because comparison is case-sensitive.
is_palindrome returns 0 immediately, even though "level" (all lowercase) would be a palindrome.
Edge cases to consider
- the empty string (a palindrome)
- a single character (a palindrome)
- even vs odd length strings
- case sensitivity and whether spaces matter
Common mistakes
- comparing from both ends but overshooting past the middle
- using
strlenoff by one when computing the far index - modifying the string while checking it
Optional extension challenge
Ignore spaces, punctuation and letter case so that 'A man a plan a canal Panama' is recognised as a palindrome.
You can re-fetch the starter code for this question: prac_q19.c.
Question 20Absolute Value Without abs
Estimated time: 8-12 minutes
A signal-processing routine needs the magnitude of a reading regardless of its sign, but the coding standard for this project forbids calling the standard library function abs. Write a function that returns the absolute value of an integer using only basic arithmetic and a comparison.
Your task is to complete the function int my_abs(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q20.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q20.c:
gcc -Wall -Wextra -o prac_q20 prac_q20.c
./prac_q20
-7
7
./prac_q20
0
0
./prac_q20
42
42Assumptions / Restrictions / Clarifications
- Do not call the library function
abs. nmay be negative, zero or positive.- You may assume
nis neverINT_MIN(whose negation would overflow). - Do not print anything inside
my_abs.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- relational comparison
- unary negation
- returning one of two branches
Worked example
For example:
For the input -7:
- -7 is less than 0, so the function returns
-(-7). -(-7)is 7, somy_absreturns 7.
Edge cases to consider
- 0, whose absolute value is 0
- a value that is already positive
- the most negative
int(excluded here because negating it overflows)
Common mistakes
- calling
abs, which the spec forbids - returning
nunchanged for negatives - using
n * -1but forgetting it still needs the sign test
Optional extension challenge
Write a my_labs for long and observe how the INT_MIN overflow problem behaves at the long boundary.
You can re-fetch the starter code for this question: prac_q20.c.
Question 21Clamp a Value to a Range
Estimated time: 8-12 minutes
A volume control must never let the level go below a minimum or above a maximum, no matter what the user requests. Write a function that clamps a value into the inclusive range [lo, hi]: values below lo become lo, values above hi become hi, and values already in range are returned unchanged.
Your task is to complete the function int clamp(int value, int lo, int hi), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q21.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q21.c:
gcc -Wall -Wextra -o prac_q21 prac_q21.c
./prac_q21
5 0 10
5
./prac_q21
-3 0 10
0
./prac_q21
15 0 10
10Assumptions / Restrictions / Clarifications
- You may assume
lo <= hi. - The range is inclusive of both
loandhi. - Do not print anything inside
clamp.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- chained if statements
- range boundary logic
- returning early
Worked example
For example:
For value = -3 with range [0, 10]:
- -3 is less than
lo(0), so the function returnslo. clamptherefore returns 0.
Edge cases to consider
valueexactly equal toloorhi(returned unchanged)valuealready inside the rangelo == hi, collapsing the range to a single allowedvalue
Common mistakes
- using exclusive comparisons and clamping a valid boundary
value - checking only one of the two bounds
- returning
lo/hiswapped
Optional extension challenge
Write a float version and a wrap version that, instead of clamping, wraps out-of-range values around the interval.
You can re-fetch the starter code for this question: prac_q21.c.
Question 22Is Leap Year
Estimated time: 8-12 minutes
A calendar application must decide whether February has 28 or 29 days. Write a function that returns 1 if the given year is a leap year and 0 otherwise.
A year is a leap year if it is divisible by 4, except that years divisible by 100 are not leap years unless they are also divisible by 400.
Your task is to complete the function int is_leap_year(int year), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q22.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q22.c:
gcc -Wall -Wextra -o prac_q22 prac_q22.c
./prac_q22
2000
1
./prac_q22
1900
0
./prac_q22
2024
1
./prac_q22
2023
0Assumptions / Restrictions / Clarifications
- You may assume year is a positive integer.
- Return exactly 1 for a leap year and 0 otherwise.
- Do not print anything inside
is_leap_year.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- the modulo operator
- combining divisibility rules
- ordering conditions correctly
Worked example
For example:
For the year 1900:
- 1900 is not divisible by 400.
- 1900 is divisible by 100, so the century rule makes it NOT a leap year.
is_leap_yearreturns 0.
Edge cases to consider
- century years like 1900 (not a leap year)
- 400-divisible years like 2000 (a leap year)
- an ordinary divisible-by-4 year like 2024
- a non-leap year like 2023
Common mistakes
- checking divisibility by 4 first and returning before applying the 100/400 rules
- getting the 100 and 400 exceptions backwards
- using
&&/||in one expression and mis-ordering the precedence
Optional extension challenge
Return the number of days in a given month of a given year, using the leap-year test for February.
You can re-fetch the starter code for this question: prac_q22.c.
Question 23Integer Power (Iterative)
Estimated time: 8-12 minutes
A fixed-point maths helper needs to raise an integer base to a non-negative integer power without calling pow from the maths library. Write a function that computes base raised to the power exp using a loop.
Your task is to complete the function int int_pow(int base, int exp), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q23.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q23.c:
gcc -Wall -Wextra -o prac_q23 prac_q23.c
./prac_q23
2 10
1024
./prac_q23
5 0
1
./prac_q23
3 4
81Assumptions / Restrictions / Clarifications
- You may assume
exp>= 0. - Recall that any
baseraised to the power 0 is 1, including 0 to the power 0 which you should treat as 1. - Do not call
powor any maths-library function. - You may assume the result fits in an
int.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- accumulating a product in a loop
- the empty-product identity
- loop counting
Worked example
For example:
For base 2 and exp 10:
- result starts at 1 and is multiplied by 2 exactly ten times.
1 * 2^10 = 1024, soint_powreturns 1024.
Edge cases to consider
exp == 0(result is 1 for anybase)base == 0with positiveexp(result 0)base == 1(result always 1)- a large
expthat risks overflow
Common mistakes
- initialising result to 0 so every answer is 0
- looping
exp + 1times (off-by-one) - returning
baseinstead of the accumulated result whenexpis 1
Optional extension challenge
Implement fast exponentiation by squaring, which uses O(log exp) multiplications instead of O(exp).
You can re-fetch the starter code for this question: prac_q23.c.
Question 24Count Even Digits
Estimated time: 10-15 minutes
A number-styling tool wants to know how many of a number's decimal digits are even (0, 2, 4, 6 or 8). Write a function that returns the count of even digits in the given integer.
The sign of a negative number should be ignored; only its digits matter.
Your task is to complete the function int count_even_digits(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q24.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q24.c:
gcc -Wall -Wextra -o prac_q24 prac_q24.c
./prac_q24
2468
4
./prac_q24
13579
0
./prac_q24
0
1
./prac_q24
-2040
4Assumptions / Restrictions / Clarifications
- A negative number has the same even-digit count as its absolute value.
- The number 0 has a single digit, which is even.
- You may assume
nis notINT_MIN. - Do not print anything inside
count_even_digits.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- digit extraction with
% 10and/ 10 - parity testing
- handling the zero case
Worked example
For example:
For the input -2040:
- the sign is ignored, leaving the digits 2, 0, 4, 0.
- all four digits (2, 0, 4, 0) are even.
count_even_digitsreturns 4.
Edge cases to consider
- 0, which is a single even digit and must return 1
- a negative number (sign ignored)
- a number with no even digits at all
- trailing zeros, which are even digits
Common mistakes
- returning 0 for the input 0 because the loop never runs
- counting the minus sign as a digit
- treating 0 as odd
Optional extension challenge
Return the count of even digits minus odd digits, so the sign of the result reveals which kind dominates.
You can re-fetch the starter code for this question: prac_q24.c.
Question 25GCD (Recursive)
Estimated time: 10-15 minutes
A fraction-simplifying routine needs the greatest common divisor of two numbers so it can reduce fractions to lowest terms. Write a RECURSIVE function that computes the greatest common divisor of two non-negative integers using Euclid's algorithm.
Euclid's algorithm relies on the fact that gcd(a, b) equals gcd(b, a % b), and that gcd(a, 0) equals a.
Your task is to complete the function int gcd(int a, int b), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q25.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q25.c:
gcc -Wall -Wextra -o prac_q25 prac_q25.c
./prac_q25
48 36
12
./prac_q25
17 5
1
./prac_q25
100 0
100
./prac_q25
13 13
13Assumptions / Restrictions / Clarifications
- You may assume
aandbare non-negative and not both zero. - Your solution must be recursive (it must call
gcd). gcd(a, 0)isa.- Do not print anything inside
gcd.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- recursion with a base case
- the modulo operator
- Euclid's algorithm
Worked example
For example:
For a = 48, b = 36:
gcd(48, 36)callsgcd(36, 48 % 36) = gcd(36, 12).gcd(36, 12)callsgcd(12, 36 % 12) = gcd(12, 0).gcd(12, 0)hits the base case and returns 12.
Edge cases to consider
b == 0(base case, returnsa)asmaller thanb(the first step swaps them)- the two numbers being equal
- one number being a multiple of the other
Common mistakes
- writing an iterative loop when a recursive solution is required
- forgetting the
b == 0base case, causing infinite recursion or a divide-by-zero - recursing with the arguments in the wrong order
Optional extension challenge
Extend it to also return the least common multiple, computed as a / gcd(a, b) * b to avoid overflow.
You can re-fetch the starter code for this question: prac_q25.c.
Question 26Sum From 1 to N (Recursive)
Estimated time: 8-12 minutes
As an exercise in recursion, write a function that returns the sum of all integers from 1 up to and including n, but you must compute it RECURSIVELY rather than with a loop or a closed-form formula.
Your task is to complete the function int sum_to_n(int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q26.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q26.c:
gcc -Wall -Wextra -o prac_q26 prac_q26.c
./prac_q26
5
15
./prac_q26
0
0
./prac_q26
100
5050Assumptions / Restrictions / Clarifications
- You may assume
n >= 0. sum_to_n(0)is 0 (the empty sum).- Your solution must be recursive and must not use the
n * (n + 1) / 2formula. - Do not print anything inside
sum_to_n.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- recursion
- identifying a base case
- building a result while unwinding
Worked example
For example:
For n = 5:
sum_to_n(5) = 5 + sum_to_n(4) = 5 + 4 + sum_to_n(3)... down tosum_to_n(0).sum_to_n(0)returns 0, unwinding to 5 + 4 + 3 + 2 + 1 + 0.- the total is 15, so
sum_to_nreturns 15.
Edge cases to consider
n == 0, the base case returning 0n == 1(a single-step recursion)- a larger
nthat produces deep recursion
Common mistakes
- missing the base case, causing infinite recursion
- using the closed-form formula when recursion is required
- recursing on
ninstead ofn - 1and never reaching the base case
Optional extension challenge
Rewrite it to sum only the even numbers from 1 to n recursively, without changing the return type.
You can re-fetch the starter code for this question: prac_q26.c.
Question 27Max Consecutive Equal Elements
Estimated time: 10-15 minutes
A monitoring dashboard wants to report the longest streak of identical readings in a log. Write a function that returns the length of the longest run of equal values that appear consecutively in an array of n integers.
Your task is to complete the function int max_run(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q27.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q27.c:
gcc -Wall -Wextra -o prac_q27 prac_q27.c
./prac_q27
6
1 1 2 2 2 3
3
./prac_q27
3
7 8 9
1
./prac_q27
0
0Assumptions / Restrictions / Clarifications
- If
nis 0 the function returns 0. - A run is a maximal sequence of equal values in adjacent positions.
- A single element counts as a run of length 1.
- Do not print anything inside
max_run.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- comparing adjacent array elements
- tracking a running and a best value
- resetting a counter
Worked example
For example:
For the array 1 1 2 2 2 3:
- the run of 1s has length 2.
- the run of 2s has length 3.
- the run of 3s has length 1.
The longest run is 3, so max_run returns 3.
Edge cases to consider
- empty array returning 0
- an array with no repeats (answer 1)
- an array where every element is equal (answer
n) - the longest run at the very end
Common mistakes
- comparing
arr[i]toarr[0]instead of the previous element - starting the run counters at 0, giving an answer one too small
- starting the loop at
i = 0and readingarr[-1]
Optional extension challenge
Also return the value that forms the longest run via an output pointer, breaking ties by earliest run.
You can re-fetch the starter code for this question: prac_q27.c.
Question 28Count Nodes In A Linked List
Estimated time: 10-15 minutes
Note prac_q28.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A playlist is stored as a linked list, one node per track, and the player needs to show how many tracks are queued. Write a function that walks a linked list and returns the number of nodes in it.
The list may be empty, in which case there are no nodes to count.
Your task is to complete the function int list_length(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q28.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q28.c:
gcc -Wall -Wextra -o prac_q28 prac_q28.c
./prac_q28
5
4 8 15 16 23
5
./prac_q28
1
7
1
./prac_q28
0
0Assumptions / Restrictions / Clarifications
- An empty list is represented by
headbeingNULL, and the answer is then 0. - Do not modify the list. Your function must not change any
nextorvaluefield. - Do not call
mallocorfreeinsidelist_length. - Do not print anything inside
list_length; only main should print.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- walking a linked list with a pointer
- using
NULLas the end-of-list marker - accumulating a count
Edge cases to consider
- an empty list, where
headisNULLand the loop body never runs - a list with exactly one node
- a long list, to make sure the loop actually advances
Common mistakes
- writing
current = current->nextinside an if instead of the loop body, so the loop never ends - starting the count at 1, which over-counts by one
- testing
current->next != NULLinstead ofcurrent != NULL, which misses the last node
Optional extension challenge
Write a second function that returns the number of nodes without using a loop, by calling itself on head->next.
You can re-fetch the starter code for this question: prac_q28.c.
Question 29Largest Value In A Linked List
Estimated time: 10-15 minutes
Note prac_q29.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A weather station appends each hour's temperature reading to a linked list. Write a function that returns the largest value stored in the list.
You may assume the list contains at least one node, so there is always an answer to return.
Your task is to complete the function int list_max(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q29.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q29.c:
gcc -Wall -Wextra -o prac_q29 prac_q29.c
./prac_q29
5
3 9 2 9 1
9
./prac_q29
4
-8 -2 -40 -5
-2
./prac_q29
1
6
6Assumptions / Restrictions / Clarifications
- The list is guaranteed to contain at least one node.
- Values may be negative, so do not assume the largest value is positive.
- Do not modify the list.
- Do not call
mallocorfreeinsidelist_max. - Do not print anything inside
list_max.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- tracking a running maximum
- seeding a comparison from the first element
- traversing from the second node onward
Edge cases to consider
- a list where every value is negative, so a 0 starting point would be wrong
- a single-node list, where the answer is that node's value
- the largest value appearing more than once
- the largest value being the very first or very last node
Common mistakes
- starting
maxat 0, which breaks on an all-negative list - starting the walk at
headinstead ofhead->nextafter seeding fromhead(harmless, but a sign the seed was forgotten) - comparing
current->next->valueand reading past the end of the list
Optional extension challenge
Return the smallest value as well, through a second int pointer parameter, in a single pass.
You can re-fetch the starter code for this question: prac_q29.c.
Question 30Search A Linked List
Estimated time: 10-15 minutes
Note prac_q30.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A door system keeps the ID of every card that is allowed through in a linked list. Write a function that reports whether a given value appears anywhere in the list.
Return 1 if the value is found and 0 if it is not.
Your task is to complete the function int list_contains(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q30.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q30.c:
gcc -Wall -Wextra -o prac_q30 prac_q30.c
./prac_q30
5
2 4 6 8 10
6
1
./prac_q30
4
1 2 3 4
9
0
./prac_q30
0
5
0Assumptions / Restrictions / Clarifications
- An empty list contains nothing, so the answer for an empty list is always 0.
- Return as soon as the
valueis found; there is no need to keep walking. - Do not modify the list.
- Do not call
mallocorfreeinsidelist_contains. - Do not print anything inside
list_contains.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- searching a linked structure
- returning early from inside a loop
- handling the not-found case after the loop
Edge cases to consider
- an empty list, which can never contain the
value - the
valuesitting in the very first node - the
valuesitting in the very last node - the
valueappearing several times, which should still return 1
Common mistakes
- returning 0 from inside the loop on the first mismatch, which only ever checks the first node
- forgetting the return after the loop, so the function falls off the end
- using
=instead of==in the comparison
Optional extension challenge
Return the position of the first match instead, counting from 0, and -1 when the value is absent.
You can re-fetch the starter code for this question: prac_q30.c.
Question 31Count Occurrences In A Linked List
Estimated time: 10-15 minutes
Note prac_q31.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A vending machine logs every coin it accepts into a linked list. Write a function that counts how many nodes hold a particular value.
If the value never appears, the count is 0.
Your task is to complete the function int count_value(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q31.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q31.c:
gcc -Wall -Wextra -o prac_q31 prac_q31.c
./prac_q31
6
1 2 1 3 1 4
1
3
./prac_q31
4
5 6 7 8
9
0
./prac_q31
0
3
0Assumptions / Restrictions / Clarifications
- An empty list gives a count of 0.
- Every matching node counts, not just the first one.
- Do not modify the list.
- Do not call
mallocorfreeinsidecount_value. - Do not print anything inside
count_value.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- counting matches while traversing
- combining a condition with an accumulator
- walking to the end rather than stopping early
Edge cases to consider
- an empty list
- no node matching at all
- every node matching
Common mistakes
- returning as soon as one match is found, which always gives 1
- resetting the counter inside the loop
- advancing the pointer only inside the if, so a non-match loops forever
Optional extension challenge
Count how many nodes hold a value strictly between two bounds passed as extra parameters.
You can re-fetch the starter code for this question: prac_q31.c.
Question 32Count Even Values In A Linked List
Estimated time: 10-15 minutes
Note prac_q32.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A turnstile records the number of people in each group entering a venue, one node per group. Write a function that counts how many nodes hold an even number.
Zero is even and must be counted.
Your task is to complete the function int count_even(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q32.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q32.c:
gcc -Wall -Wextra -o prac_q32 prac_q32.c
./prac_q32
6
1 2 3 4 5 6
3
./prac_q32
4
-4 -3 0 7
2
./prac_q32
0
0Assumptions / Restrictions / Clarifications
- Zero counts as even.
- Negative even numbers count too, so -4 is even.
- Do not modify the list.
- Do not call
mallocorfreeinsidecount_even. - Do not print anything inside
count_even.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- the remainder operator on list data
- counting under a condition
- reasoning about negative operands
Edge cases to consider
- a list containing 0, which is even
- negative even values such as -4
- an empty list
Common mistakes
- writing
value % 2 == 1to test for odd, which is false for negative odd numbers in C - counting odd values by mistake
- forgetting that 0 is even
Optional extension challenge
Return the count of odd values as well, through an int pointer parameter.
You can re-fetch the starter code for this question: prac_q32.c.
Question 33Sum A Linked List From A Position
Estimated time: 15-20 minutes
Note prac_q33.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A bank statement is stored newest-first as a linked list. Write a function that adds up every value from a given position to the end of the list, counting positions from 0.
If the position is past the end of the list there is nothing to add, and the total is 0.
Your task is to complete the function int sum_from(struct node *head, int position), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q33.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q33.c:
gcc -Wall -Wextra -o prac_q33 prac_q33.c
./prac_q33
5
1 2 3 4 5
2
12
./prac_q33
4
10 20 30 40
0
100
./prac_q33
3
1 2 3
9
0Assumptions / Restrictions / Clarifications
- Positions are counted from 0, so
position0 means the whole list. - A
positionpast the end of the list gives a total of 0. - You may assume
positionis not negative. - Do not modify the list.
- Do not print anything inside
sum_from.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- skipping a fixed number of nodes before working
- guarding a walk against running off the end
- two loops over one traversal
Edge cases to consider
- a
positionof 0, which sums the entire list - a
positionpast the end, which sums nothing - a
positionlanding exactly on the last node
Common mistakes
- dropping the
current != NULLtest in the skip loop and dereferencingNULL - starting the index at 1, which is off by one
- summing while skipping, which double-counts the front of the list
Optional extension challenge
Take an end position too, and sum only the nodes between the two positions.
You can re-fetch the starter code for this question: prac_q33.c.
Question 34Value At A Position
Estimated time: 10-15 minutes
Note prac_q34.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A queue of print jobs is held in a linked list. Write a function that returns the value stored at a given position, counting from 0.
If the position does not exist in the list, return -1.
Your task is to complete the function int value_at(struct node *head, int position), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q34.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q34.c:
gcc -Wall -Wextra -o prac_q34 prac_q34.c
./prac_q34
5
7 8 9 10 11
2
9
./prac_q34
3
4 5 6
0
4
./prac_q34
3
4 5 6
7
-1Assumptions / Restrictions / Clarifications
- Positions count from 0, so
position0 is the first node. - Return -1 if the
positionis past the end of the list. - You may assume
positionis not negative. - Do not modify the list.
- Do not print anything inside
value_at.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- keeping an index alongside a pointer
- returning from inside a traversal
- signalling not-found with a sentinel
Edge cases to consider
position0, the first node- the last valid
position - a
positionpast the end, which returns -1 - an empty list, which always returns -1
Common mistakes
- counting positions from 1 instead of 0
- checking
index == positionafter advancing, which returns the wrong node - forgetting the -1 return, so the function falls off the end
Optional extension challenge
Return the value at a position counted from the END of the list instead.
You can re-fetch the starter code for this question: prac_q34.c.
Question 35Add To The Front Of A Linked List
Estimated time: 10-15 minutes
Note prac_q35.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A browser keeps its history newest-first in a linked list. Write a function that adds a new node holding a given value to the front of the list and returns the new head.
Adding to the front works even when the list is empty.
Your task is to complete the function struct node *push_front(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q35.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q35.c:
gcc -Wall -Wextra -o prac_q35 prac_q35.c
./prac_q35
4
2 3 4 5
1
1 2 3 4 5
./prac_q35
0
9
9
./prac_q35
1
5
7
7 5Assumptions / Restrictions / Clarifications
- Allocate the new node with
malloc. - Return the new
headof the list, which is the node you just created. - Adding to an empty list gives a list of one node.
- Do not print anything inside
push_front.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- allocating a node with
malloc - pointing a new node at an existing list
- returning a new
head
Edge cases to consider
- an empty list, where the new node becomes the whole list
- a list of one node
- checking the old
headis still reachable through the new node
Common mistakes
- setting
new_node->nexttoNULL, which throws the rest of the list away - forgetting to return the new node, so the caller keeps the old
head - assigning to
headinside the function and expecting the caller to see it
Optional extension challenge
Write the matching pop_front, which removes and frees the first node and returns the new head.
You can re-fetch the starter code for this question: prac_q35.c.
Question 36Add To The End Of A Linked List
Estimated time: 15-20 minutes
Note prac_q36.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A ticket queue is stored as a linked list with the next person to serve at the front. Write a function that adds a node holding a given value to the END of the list and returns the head.
If the list is empty the new node becomes the head.
Your task is to complete the function struct node *push_back(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q36.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q36.c:
gcc -Wall -Wextra -o prac_q36 prac_q36.c
./prac_q36
3
1 2 3
4
1 2 3 4
./prac_q36
0
7
7
./prac_q36
1
5
6
5 6Assumptions / Restrictions / Clarifications
- Allocate the new node with
mallocand set itsnexttoNULL. - If the list is empty, return the new node as the
head. - Otherwise return the original
headunchanged. - Do not print anything inside
push_back.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- walking to the last node
- the difference between
currentandcurrent->nextas a loop test - treating the empty list as a special case
Edge cases to consider
- an empty list, which needs its own branch
- a list of one node
- confirming the new node's next is
NULLso the list still ends
Common mistakes
- looping while
current != NULL, which walks past the last node and then dereferencesNULL - forgetting the empty-list case and dereferencing a
NULLhead - returning the new node instead of the original
headon a non-empty list
Optional extension challenge
Keep a tail pointer so appending does not need to walk the list every time.
You can re-fetch the starter code for this question: prac_q36.c.
Question 37Delete The First Node
Estimated time: 10-15 minutes
Note prac_q37.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A task list is stored as a linked list and the front task has just been completed. Write a function that removes the first node, frees it, and returns the new head.
Deleting from an empty list leaves it empty.
Your task is to complete the function struct node *delete_first(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q37.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q37.c:
gcc -Wall -Wextra -o prac_q37 prac_q37.c
./prac_q37
5
1 2 3 4 5
2 3 4 5
./prac_q37
1
9
./prac_q37
0Assumptions / Restrictions / Clarifications
- Call
freeon the node you remove. - Return the new
head, which is the second node of the original list. - If the list is empty, return
NULL. - If the list had one node, the result is an empty list, so return
NULL. - Do not print anything inside
delete_first.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- freeing a node without losing the rest of the list
- saving a pointer before destroying what holds it
- returning an updated
head
Edge cases to consider
- an empty list, which must not be dereferenced
- a one-node list, which becomes empty
- confirming the freed node is not read afterwards
Common mistakes
- calling
free(head)before readinghead->next, which reads freed memory - forgetting to
freeat all, which leaks the node - returning
headinstead of the newhead, leaving a dangling pointer
Optional extension challenge
Delete the first node only when it holds a given value, otherwise leave the list alone.
You can re-fetch the starter code for this question: prac_q37.c.
Question 38Count Values Above A Threshold
Estimated time: 10-15 minutes
Note prac_q38.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A monitoring system stores each temperature reading in a linked list. Write a function that counts how many nodes hold a value strictly greater than a given limit.
A value exactly equal to the limit does not count.
Your task is to complete the function int count_above(struct node *head, int limit), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q38.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q38.c:
gcc -Wall -Wextra -o prac_q38 prac_q38.c
./prac_q38
6
1 5 9 5 12 3
5
2
./prac_q38
3
1 2 3
10
0
./prac_q38
0
0
0Assumptions / Restrictions / Clarifications
- Only values strictly greater than
limitcount; equal values do not. - An empty list gives 0.
- Do not modify the list.
- Do not print anything inside
count_above.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- applying a comparison to every node
- the difference between
>and>= - counting under a condition
Edge cases to consider
- values exactly equal to the
limit, which must not count - no value above the
limitat all - a negative
limit
Common mistakes
- using
>=and counting the boundary value - comparing against the wrong operand order
- returning early on the first value above the
limit
Optional extension challenge
Count values below a second limit too, so the function reports how many fall outside a range.
You can re-fetch the starter code for this question: prac_q38.c.
Question 39Sum Of Negative Values In A Linked List
Estimated time: 10-15 minutes
Note prac_q39.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
An account keeps every transaction in a linked list, with withdrawals stored as negative numbers. Write a function that adds up only the negative values and returns that total.
The total will be zero or negative. If there are no withdrawals, return 0.
Your task is to complete the function int sum_negative(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q39.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q39.c:
gcc -Wall -Wextra -o prac_q39 prac_q39.c
./prac_q39
6
10 -5 3 -20 0 -1
-26
./prac_q39
3
1 2 3
0
./prac_q39
0
0Assumptions / Restrictions / Clarifications
- Zero is not negative and must not be included.
- If no value is negative, return 0.
- Do not modify the list.
- Do not print anything inside
sum_negative.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- filtering while accumulating
- signed arithmetic on a running total
- distinguishing zero from negative
Edge cases to consider
- a list with no negative values, giving 0
- a list containing 0, which must not be added
- an empty list
Common mistakes
- using
<= 0and folding zeroes in, which does not change the total but shows the wrong intent - adding the absolute value and returning a positive number
- starting the total at the first value instead of 0
Optional extension challenge
Return the count of withdrawals as well, through an int pointer parameter.
You can re-fetch the starter code for this question: prac_q39.c.
Question 40Last Value In A Linked List
Estimated time: 10-15 minutes
Note prac_q40.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A sensor appends each new reading to the end of a linked list, so the most recent reading is the last node. Write a function that returns the value in the last node.
You may assume the list has at least one node.
Your task is to complete the function int last_value(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q40.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q40.c:
gcc -Wall -Wextra -o prac_q40 prac_q40.c
./prac_q40
4
3 6 9 12
12
./prac_q40
1
42
42
./prac_q40
2
-7 -8
-8Assumptions / Restrictions / Clarifications
- The list is guaranteed to have at least one node.
- A one-node list means the first node is also the last.
- Do not modify the list.
- Do not print anything inside
last_value.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- stopping ON the last node rather than past it
current->nextas the loop condition- when a single node is both first and last
Edge cases to consider
- a one-node list, where the loop never runs
- a two-node list, the smallest case where the loop runs once
- confirming the pointer is not
NULLwhen the value is read
Common mistakes
- looping while
current != NULL, which ends withcurrentasNULLand then crashes - returning
head->value, which is the first node - keeping a previous pointer
currentthat is never needed here
Optional extension challenge
Return the last value without a loop, by having the function call itself on head->next.
You can re-fetch the starter code for this question: prac_q40.c.
Question 41Count Words in a Sentence
Estimated time: 12-18 minutes
A basic writing-assistant tool wants to give users a live word count as they type a sentence. Write a function that counts the number of words in a null-terminated string, where words are separated by one or more single space characters.
Leading and trailing spaces should not create extra (empty) words, and runs of multiple consecutive spaces between words should still only count as a single separator.
Your task is to complete the function int count_words(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q41.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q41.c:
gcc -Wall -Wextra -o prac_q41 prac_q41.c
./prac_q41
the quick brown fox
4
./prac_q41
hello world
2
./prac_q41
0
./prac_q41
0Assumptions / Restrictions / Clarifications
- Words are only separated by the space character
' ', not tabs or other whitespace. - An empty string, or a string containing only spaces, has 0 words.
- Multiple consecutive spaces between words do not create extra empty words.
- Punctuation attached to a word (e.g. "fox.") is still counted as part of that word.
- Do not call
scanf,getcharorfgetsinsidecount_words; the string is passed in as an argument.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- scanning a string for word boundaries
- tracking whether you are 'inside' a word
- handling runs of separators
Worked example
For example:
For the string " hello world ":
- the leading spaces are skipped (no word starts there).
- 'hello' is the first word.
- the run of 3 spaces between the words is treated as a single separator.
- 'world' is the second word.
- the trailing space does not start a new word.
There are 2 words in total, so count_words returns 2.
Edge cases to consider
- the empty string (0 words)
- a string that is all spaces (still 0 words)
- leading and trailing spaces that must not create empty words
- multiple consecutive spaces between two words
Common mistakes
- counting spaces instead of words, giving one too many/few
- double-counting a word when several spaces separate words
- off-by-one from not handling the transition from space to non-space cleanly
Optional extension challenge
Extend it to also treat tab and newline characters as word separators, not just the space character.
You can re-fetch the starter code for this question: prac_q41.c.
Question 42Struct Rectangle Area and Perimeter
Estimated time: 12-18 minutes
Note prac_q42.c uses the following data type:
struct rectangle {
int width;
int height;
};A simple floor-planning tool represents each room as a rectangle and needs to report both its floor area (for cost estimation) and its perimeter (for skirting board length). A struct rectangle (see the provided starter header) stores an integer width and height.
Write two functions: one that computes the area of a rectangle, and one that computes its perimeter.
Your task is to complete the functions int rect_area(struct rectangle r) and int rect_perimeter(struct rectangle r), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q42.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q42.c:
gcc -Wall -Wextra -o prac_q42 prac_q42.c
./prac_q42
4 5
20 18
./prac_q42
3 3
9 12
./prac_q42
1 10
10 22Assumptions / Restrictions / Clarifications
widthandheightare always positive.- The rectangle is passed by value, so the functions should not need to modify it.
- Area is
width * height; perimeter is2 * (width + height).
Stuck? Here's a hint
What this practises
This question gives you practice with:
- accessing
structfields with the.operator - passing a
structby value - computing geometric properties from record fields
Worked example
For example:
For width = 4 and height = 5:
area = width * height = 4 * 5 = 20.perimeter = 2 * (width + height) = 2 * 9 = 18.
main prints "20 18".
Edge cases to consider
- a square (
width == height) - a rectangle where width and height differ significantly
- large dimensions where area may exceed
intrange
Common mistakes
- confusing the area and perimeter formulas
- using
->on astructpassed by value instead of. - swapping width and height in calculations
Optional extension challenge
Add a function that returns 1 if one rectangle fits entirely inside another (ignoring rotation).
You can re-fetch the starter code for this question: prac_q42.c.
Question 43Struct Distance Between Two Points
Estimated time: 12-18 minutes
Note prac_q43.c uses the following data type:
struct point {
int x;
int y;
};A simple 2D geometry library needs a way to measure how far apart two points are, for example to compute how far a game character has moved. A struct point represents a location in 2D space with integer x and y coordinates (see the provided starter header). Write a function that computes the Euclidean separation between two points as a double.
Both points are passed by value (as whole structs), so your function should not need to use pointers to access their fields.
Your task is to complete the function double distance(struct point p1, struct point p2), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q43.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q43.c:
gcc -Wall -Wextra -o prac_q43 prac_q43.c -lm
./prac_q43
0 0 3 4
5
./prac_q43
-2 -2 2 1
5
./prac_q43
7 7 7 7
0Assumptions / Restrictions / Clarifications
- The formula is
sqrt((x2 - x1)^2 + (y2 - y1)^2). - You must
#include <math.h>to usesqrt, and link with-lmif compiling manually. xandycoordinates may be negative, zero, or positive.p1andp2may refer to the same coordinates, in which case the result is 0.
Stuck? Here's a hint
What this practises
This question gives you practice with:
structfield access- the Euclidean-
distanceformula - using
sqrtfrom<math.h>
Worked example
For example:
For p1 = (0, 0) and p2 = (3, 4):
dx = 3 - 0 = 3,dy = 4 - 0 = 4.dx^2 + dy^2 = 9 + 16 = 25.sqrt(25) = 5.
distance returns 5.0, matching the classic 3-4-5 right triangle.
Edge cases to consider
- two identical points (result 0)
- points differing only in x or only in y
- points with negative coordinates
- large coordinates where squaring may overflow
int
Common mistakes
- forgetting to link the math library (
-lm) when usingsqrt - squaring the difference incorrectly or omitting one axis
- using
intarithmetic and losing the fractional result
Optional extension challenge
Add a function that, given an array of points, returns the pair that are closest together.
You can re-fetch the starter code for this question: prac_q43.c.
Question 44Compass Direction Turn
Estimated time: 10-15 minutes
Note prac_q44.c uses the following data type:
enum direction {
NORTH,
EAST,
SOUTH,
WEST
};A robot navigation module tracks which way a robot is facing using the four compass points. Write a function that, given the current facing direction, returns the direction after turning 90 degrees clockwise (to the right).
The compass points are defined by an enum in clockwise order, so turning right advances to the next point and wraps around from WEST back to NORTH.
Your task is to complete the function enum direction turn_right(enum direction d), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q44.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q44.c:
gcc -Wall -Wextra -o prac_q44 prac_q44.c
./prac_q44
0 1
EAST
./prac_q44
0 4
NORTH
./prac_q44
3 1
NORTHAssumptions / Restrictions / Clarifications
- The only valid directions are the four
enum directionvaluesNORTH,EAST,SOUTHandWEST. - Turning right from
WESTwraps around toNORTH. - Do not print anything inside
turn_right. - You must return an
enum directionvalue, not a plainintprinted by the function.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- enum values as small integers
- modular wrap-around
- returning an enum type
Worked example
For example:
For starting direction 3 (WEST) and 1 turn:
turn_right(WEST)advances to(3 + 1) % 4 = 0.- direction 0 is
NORTH, so the program printsNORTH.
Edge cases to consider
- turning right from WEST, which must wrap to NORTH
- zero turns (direction unchanged)
- four turns returning to the start
- a full multiple of four turns
Common mistakes
- forgetting the
% 4soWEST + 1becomes an invalid direction - returning an
intwithout the enum semantics of wrapping - turning left (subtracting) instead of right
Optional extension challenge
Add turn_left and a turn_to function that returns the fewest 90-degree turns needed to face a target direction.
You can re-fetch the starter code for this question: prac_q44.c.
Question 45Translate a Point
Estimated time: 10-15 minutes
Note prac_q45.c uses the following data type:
typedef struct {
int x;
int y;
} Point;A 2D drawing library represents positions with a Point type and needs to move points around the canvas. Write a function that returns a new Point obtained by shifting the given point by dx along the x axis and dy along the y axis.
The Point type is a typedef, so you can use it directly as Point without writing struct each time.
Your task is to complete the function Point translate(Point p, int dx, int dy), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q45.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q45.c:
gcc -Wall -Wextra -o prac_q45 prac_q45.c
./prac_q45
1 2 3 4
(4, 6)
./prac_q45
0 0 -1 -1
(-1, -1)
./prac_q45
5 5 0 0
(5, 5)Assumptions / Restrictions / Clarifications
- Return a new
Point; you do not need to modify the caller's original point. dxanddymay be negative, moving the point left or down.- Do not print anything inside
translate.
Stuck? Here's a hint
What this practises
This question gives you practice with:
typedef structtypes- passing and returning a struct by value
- field access with the
.operator
Worked example
For example:
For the point (1, 2) with offsets dx = 3, dy = 4:
- the new
xis1 + 3 = 4. - the new
yis2 + 4 = 6. translatereturns thePoint(4, 6).
Edge cases to consider
- a zero offset (point unchanged)
- negative offsets moving the point left/down
- offsets that move a coordinate below zero into negative space
Common mistakes
- writing
struct Pointwhen the typedef makesPointsufficient (and there is no tag) - modifying the caller's copy expecting it to persist (it is passed by value)
- swapping
dx/dyor x/y in the assignment
Optional extension challenge
Add scale(Point p, int factor) and manhattan_distance(Point a, Point b) using the same Point type.
You can re-fetch the starter code for this question: prac_q45.c.
Question 46Delete The Last Node
Estimated time: 15-20 minutes
Note prac_q46.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
An undo stack is stored as a linked list with the oldest action at the front. Write a function that removes the LAST node, frees it, and returns the head of the list.
Removing the last node of a one-node list leaves the list empty.
Your task is to complete the function struct node *delete_last(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q46.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q46.c:
gcc -Wall -Wextra -o prac_q46 prac_q46.c
./prac_q46
4
1 2 3 4
1 2 3
./prac_q46
1
9
./prac_q46
0Assumptions / Restrictions / Clarifications
- Call
freeon the node you remove. - If the list is empty, return
NULL. - If the list has exactly one node,
freeit and returnNULL. - Otherwise the
headdoes not change, so return it. - Do not print anything inside
delete_last.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- stopping one node early in a traversal
- looking two nodes ahead safely
- handling empty and single-node lists separately
Edge cases to consider
- an empty list
- a one-node list, which becomes empty and returns
NULL - a two-node list, the smallest case where the loop matters
Common mistakes
- walking to the last node instead of the second-last, leaving no way to unlink it
- forgetting to set
current->nexttoNULL, leaving a pointer to freed memory - testing
current->next->nextwithout first ruling out the one-node list
Optional extension challenge
Return the value that was removed through an int pointer parameter, as well as the new head.
You can re-fetch the starter code for this question: prac_q46.c.
Question 47Count Nodes Before A Value
Estimated time: 15-20 minutes
Note prac_q47.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A race result is stored as a linked list of runner numbers in finishing order. Write a function that counts how many runners finished before a given runner number.
If the number does not appear in the list, return -1.
Your task is to complete the function int count_before(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q47.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q47.c:
gcc -Wall -Wextra -o prac_q47 prac_q47.c
./prac_q47
5
8 3 5 9 1
9
3
./prac_q47
4
2 4 6 8
2
0
./prac_q47
3
1 2 3
7
-1Assumptions / Restrictions / Clarifications
- Return -1 if the
valuenever appears in the list. - Only the FIRST occurrence matters, so stop counting there.
- If the
valueis in the first node, the answer is 0. - Do not modify the list.
- Do not print anything inside
count_before.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- combining a search with a count
- returning mid-traversal
- using a sentinel for the absent case
Edge cases to consider
- the
valuein the first node, giving 0 - the
valueabsent, giving -1 - the
valueappearing more than once, where only the first counts - an empty list
Common mistakes
- incrementing the counter before the comparison, which is off by one
- returning 0 rather than -1 when the
valueis missing, which is indistinguishable from a first-node hit - continuing past the first match and counting later occurrences
Optional extension challenge
Count how many nodes come AFTER the first occurrence instead.
You can re-fetch the starter code for this question: prac_q47.c.
Question 48Is A Linked List Increasing
Estimated time: 15-20 minutes
Note prac_q48.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A leaderboard is stored as a linked list and should always be in strictly increasing order. Write a function that returns 1 if every value is strictly greater than the one before it, and 0 otherwise.
A list with fewer than two nodes is trivially in order, so return 1.
Your task is to complete the function int is_increasing(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q48.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q48.c:
gcc -Wall -Wextra -o prac_q48 prac_q48.c
./prac_q48
5
1 3 7 8 20
1
./prac_q48
4
1 5 5 9
0
./prac_q48
0
1Assumptions / Restrictions / Clarifications
- Strictly increasing means equal neighbours are NOT in order, so 3 3 returns 0.
- An empty list and a one-node list both return 1.
- Do not modify the list.
- Do not use an array, and do not call
malloc. - Do not print anything inside
is_increasing.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- comparing each node with its successor
- safely looking one node ahead
- strict versus non-strict ordering
Edge cases to consider
- equal neighbours, which break strict order
- an empty list and a one-node list, both in order
- a list that only breaks order at the very last pair
- a decreasing list
Common mistakes
- using
<instead of<=for the failure test, which wrongly accepts duplicates - looping while
current != NULLand then dereferencingcurrent->nexton the last node - returning 1 from inside the loop on the first ordered pair
Optional extension challenge
Return 1 for a list that is sorted in EITHER direction, increasing or decreasing.
You can re-fetch the starter code for this question: prac_q48.c.
Question 49Delete Every Node With A Value
Estimated time: 20-25 minutes
Note prac_q49.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A spam filter keeps a linked list of message IDs and needs to purge every entry matching a blocked ID. Write a function that removes ALL nodes holding a given value, frees them, and returns the head of the resulting list.
If every node matches, the result is an empty list.
Your task is to complete the function struct node *delete_all(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q49.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q49.c:
gcc -Wall -Wextra -o prac_q49 prac_q49.c
./prac_q49
7
1 2 1 3 1 4 1
1
2 3 4
./prac_q49
3
5 5 5
5
./prac_q49
4
1 2 3 4
9
1 2 3 4Assumptions / Restrictions / Clarifications
- Every matching node must be removed, not just the first.
- Call
freeon each node you remove. - If the list becomes empty, return
NULL. - Matching nodes may be at the front, the end, or adjacent to each other.
- Do not print anything inside
delete_all.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- unlinking a node using the one before it
- removing several nodes in one pass
- advancing only when nothing was removed
Edge cases to consider
- matches at the very front, which move the
head - every node matching, leaving an empty list
- two matching nodes next to each other
- no match at all, leaving the list unchanged
Common mistakes
- advancing the pointer after a deletion, which skips the node that moved up
- handling front matches with the same loop as the rest and losing the
head - freeing the node before reading its next pointer
Optional extension challenge
Return the number of nodes removed through an int pointer parameter.
You can re-fetch the starter code for this question: prac_q49.c.
Question 50Delete The Node At A Position
Estimated time: 20-25 minutes
Note prac_q50.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A playlist is a linked list and the user has asked to remove the track at a given position, counting from 0. Write a function that removes and frees that node and returns the head of the list.
If the position does not exist, leave the list unchanged.
Your task is to complete the function struct node *delete_at(struct node *head, int position), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q50.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q50.c:
gcc -Wall -Wextra -o prac_q50 prac_q50.c
./prac_q50
5
10 20 30 40 50
2
10 20 40 50
./prac_q50
3
1 2 3
0
2 3
./prac_q50
3
1 2 3
7
1 2 3Assumptions / Restrictions / Clarifications
- Positions count from 0, so
position0 removes the first node. - If
positionis past the end of the list, change nothing and return thehead. - You may assume
positionis not negative. - Call
freeon the node you remove. - Do not print anything inside
delete_at.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- stopping at the node BEFORE the one to remove
position0 as a special case- leaving the list alone when an index is out of range
Edge cases to consider
position0, which changes thehead- the last valid
position - a
positionpast the end, which changes nothing - an empty list
Common mistakes
- walking to the target node instead of the one before it, leaving nothing to unlink it with
- forgetting the
position0 case and returning aheadthat has been freed - not checking for the end of the list and dereferencing
NULL
Optional extension challenge
Insert a value at a given position instead, shifting the rest of the list along.
You can re-fetch the starter code for this question: prac_q50.c.
Question 51Second Largest Value In A Linked List
Estimated time: 20-25 minutes
Note prac_q51.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A scoreboard is stored as a linked list and needs to show the runner-up as well as the winner. Write a function that returns the second largest DISTINCT value in the list.
If every node holds the same value there is no runner-up, so return -1.
Your task is to complete the function int second_largest(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q51.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q51.c:
gcc -Wall -Wextra -o prac_q51 prac_q51.c
./prac_q51
6
4 9 2 9 7 1
7
./prac_q51
3
5 5 5
-1
./prac_q51
4
-3 -8 -1 -1
-3Assumptions / Restrictions / Clarifications
- Distinct means duplicates of the largest value do not count as second, so 9 9 4 gives 4.
- If the list has fewer than two distinct values, return -1.
- Values may be negative.
- Do not modify the list, do not use an array, and do not call
malloc. - Do not print anything inside
second_largest.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- two passes over one list
- tracking a best-so-far under a constraint
- distinguishing no-answer from a real answer
Edge cases to consider
- the largest value appearing several times
- every value identical, which has no runner-up
- all-negative values, where a 0 starting point would be wrong
- a two-node list
Common mistakes
- initialising second to 0 and returning it for an all-negative list
- treating a duplicate of the largest as the runner-up
- returning -1 when -1 is a legitimate answer, which this spec accepts as a known limitation
Optional extension challenge
Return the Nth largest distinct value, with N as a parameter.
You can re-fetch the starter code for this question: prac_q51.c.
Question 52Copy A Linked List
Estimated time: 20-25 minutes
Note prac_q52.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
Before running a risky operation, a program wants a private copy of a linked list so the original is untouched. Write a function that builds and returns a brand new list holding the same values in the same order.
The copy must be independent: changing one list must not affect the other.
Your task is to complete the function struct node *copy_list(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q52.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q52.c:
gcc -Wall -Wextra -o prac_q52 prac_q52.c
./prac_q52
4
1 2 3 4
1 2 3 4
./prac_q52
1
9
9
./prac_q52
0Assumptions / Restrictions / Clarifications
- Every node in the copy must be freshly allocated with
malloc. - Do not reuse any node from the original list.
- Copying an empty list gives an empty list, so return
NULL. - Do not modify the original list.
- Do not print anything inside
copy_list.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- building a new list while reading another
- keeping a tail pointer to append in order
- the difference between copying a pointer and copying a node
Edge cases to consider
- an empty list, which copies to
NULL - a one-node list
- checking the copy survives after the original is freed
Common mistakes
- returning
head, which shares nodes instead of copying them - building the copy in reverse by pushing each node onto the front
- forgetting to set the last node's
nexttoNULL
Optional extension challenge
Copy the list in reverse order instead, without a second pass.
You can re-fetch the starter code for this question: prac_q52.c.
Question 53Are Two Linked Lists Equal
Estimated time: 20-25 minutes
Note prac_q53.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A sync tool compares a local list with a remote one to decide whether an upload is needed. Write a function that returns 1 if two linked lists hold exactly the same values in the same order, and 0 otherwise.
Lists of different lengths are never equal.
Your task is to complete the function int lists_equal(struct node *a, struct node *b), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q53.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q53.c:
gcc -Wall -Wextra -o prac_q53 prac_q53.c
./prac_q53
3
1 2 3
3
1 2 3
1
./prac_q53
3
1 2 3
4
1 2 3 4
0
./prac_q53
0
0
1Assumptions / Restrictions / Clarifications
- Two empty lists are equal, so return 1.
- Lists of different lengths are not equal, even if one is a prefix of the other.
- Do not modify either list.
- Do not use an array, and do not call
malloc. - Do not print anything inside
lists_equal.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- walking two lists in step
- deciding what to do when one ends first
- two empty lists as the base case
Edge cases to consider
- one list being a prefix of the other, which is NOT equal
- two empty lists, which are equal
- same length but one differing value
- one empty and one not
Common mistakes
- stopping when either list ends and returning 1, which calls a prefix equal
- comparing pointers instead of values
- advancing only one of the two pointers
Optional extension challenge
Return 1 when one list is a prefix of the other, rather than requiring exact equality.
You can re-fetch the starter code for this question: prac_q53.c.
Question 54Join Two Linked Lists
Estimated time: 20-25 minutes
Note prac_q54.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
Two queues are being merged into one. Write a function that attaches the second list to the end of the first and returns the head of the combined list.
No new nodes should be created: the existing nodes are relinked.
Your task is to complete the function struct node *join_lists(struct node *a, struct node *b), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q54.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q54.c:
gcc -Wall -Wextra -o prac_q54 prac_q54.c
./prac_q54
3
1 2 3
2
8 9
1 2 3 8 9
./prac_q54
0
3
4 5 6
4 5 6
./prac_q54
2
7 8
0
7 8Assumptions / Restrictions / Clarifications
- Do not call
malloc; reuse the existing nodes. - If the first list is empty, the result is simply the second list.
- If the second list is empty, the result is the first list unchanged.
- The result must be a single list ending in
NULL. - Do not print anything inside
join_lists.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- relinking rather than reallocating
- walking to a tail before attaching
- empty lists on either side
Edge cases to consider
- an empty first list, where the answer is just the second
- an empty second list, which changes nothing
- both empty, giving an empty list
- checking the joined list is freed exactly once
Common mistakes
- looping while
current != NULLand losing the node to attach to - returning
binstead ofafor a non-empty first list - freeing both lists separately afterwards, which double-frees the shared nodes
Optional extension challenge
Join the lists alternately instead, taking one node from each in turn.
You can re-fetch the starter code for this question: prac_q54.c.
Question 55Insert At A Position
Estimated time: 20-25 minutes
Note prac_q55.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A queue system lets staff insert an urgent job partway down the list. Write a function that inserts a new node holding a given value so that it ends up at a given position, counting from 0, and returns the head.
If the position is past the end of the list, add the new node at the end.
Your task is to complete the function struct node *insert_at(struct node *head, int position, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q55.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q55.c:
gcc -Wall -Wextra -o prac_q55 prac_q55.c
./prac_q55
4
1 2 4 5
2
3
1 2 3 4 5
./prac_q55
3
1 2 3
0
9
9 1 2 3
./prac_q55
2
1 2
7
8
1 2 8Assumptions / Restrictions / Clarifications
- Positions count from 0, so
position0 puts the new node at the front. - A
positionpast the end of the list appends to the end. - You may assume
positionis not negative. - Allocate the new node with
malloc. - Do not print anything inside
insert_at.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- stopping at the node before an index
- wiring a new node in without losing the tail
- front and past-the-end as special cases
Edge cases to consider
position0, which changes thehead- a
positionpast the end, which appends - inserting into an empty list
- inserting immediately after the
head
Common mistakes
- setting
current->nextbefore saving the old link, which drops the rest of the list - forgetting the
position0 case and returning aheadthe new node is not part of - walking to the target index rather than one before it
Optional extension challenge
Insert so the list stays sorted, ignoring the position argument entirely.
You can re-fetch the starter code for this question: prac_q55.c.
Question 56Remove Duplicates From A Sorted Linked List
Estimated time: 20-25 minutes
Note prac_q56.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A sorted list of student IDs has picked up repeats after a merge. Write a function that removes every repeated node so each value appears exactly once, and returns the head.
The list is already sorted, so equal values are always next to each other.
Your task is to complete the function struct node *remove_duplicates(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q56.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q56.c:
gcc -Wall -Wextra -o prac_q56 prac_q56.c
./prac_q56
7
1 1 2 3 3 3 4
1 2 3 4
./prac_q56
3
5 5 5
5
./prac_q56
0Assumptions / Restrictions / Clarifications
- You may assume the list is sorted in non-decreasing order.
- Keep the FIRST of each run of equal values and
freethe rest. - Call
freeon every node you remove. - An empty list stays empty.
- Do not print anything inside
remove_duplicates.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- exploiting sortedness to find duplicates cheaply
- removing without a previous pointer
- advancing only when nothing was removed
Edge cases to consider
- a run of three or more equal values
- every value identical, leaving one node
- no duplicates at all
- an empty list
Common mistakes
- advancing after a removal, which skips the node that moved up and leaves runs of three
- comparing
currentwithcurrent->next->next - forgetting to
freethe removed nodes
Optional extension challenge
Remove duplicates from an UNSORTED list, which needs a different approach entirely.
You can re-fetch the starter code for this question: prac_q56.c.
Question 57Delete The Second Last Node
Estimated time: 20-25 minutes
Note prac_q57.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
An editor keeps a linked list of saved revisions and wants to discard the one just before the newest. Write a function that removes the second-last node, frees it, and returns the head.
A list with fewer than two nodes has no second-last node, so leave it unchanged.
Your task is to complete the function struct node *delete_second_last(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q57.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q57.c:
gcc -Wall -Wextra -o prac_q57 prac_q57.c
./prac_q57
5
1 2 3 4 5
1 2 3 5
./prac_q57
2
7 8
8
./prac_q57
1
9
9Assumptions / Restrictions / Clarifications
- If the list has fewer than two nodes, change nothing.
- In a two-node list the second-last node is the
head. - Call
freeon the node you remove. - Do not print anything inside
delete_second_last.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- looking three nodes ahead safely
- a two-node list as its own case
- leaving short lists untouched
Edge cases to consider
- a two-node list, where the
headis removed - a one-node list and an empty list, both unchanged
- a three-node list, the smallest case where the loop runs
Common mistakes
- dereferencing
current->next->next->nextwithout ruling out the short lists first - removing the last node instead of the second-last
- forgetting that a two-node list changes the
head
Optional extension challenge
Remove the Nth node from the end, with N as a parameter.
You can re-fetch the starter code for this question: prac_q57.c.
Question 58Count Distinct Values In A Linked List
Estimated time: 20-25 minutes
Note prac_q58.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A survey stores every response in a linked list and needs to know how many different answers were given. Write a function that counts how many DISTINCT values appear in the list.
The list is not sorted, so equal values may be anywhere.
Your task is to complete the function int count_distinct(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q58.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q58.c:
gcc -Wall -Wextra -o prac_q58 prac_q58.c
./prac_q58
6
1 2 1 3 2 4
4
./prac_q58
3
5 5 5
1
./prac_q58
0
0Assumptions / Restrictions / Clarifications
- A value that appears several times counts once.
- An empty list has 0 distinct values.
- Do not modify the list, do not use an array, and do not call
malloc. - Do not print anything inside
count_distinct.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- a nested traversal of the same list
- counting the first occurrence only
- using the outer pointer as a stopping point for the inner loop
Edge cases to consider
- every value the same, giving 1
- every value different
- an empty list, giving 0
Common mistakes
- running the inner loop over the whole list, so every value matches itself and nothing is counted
- comparing against nodes AFTER the current one, which counts the last occurrence and is easy to get subtly wrong
- forgetting to reset the seen flag for each outer node
Optional extension challenge
Return the value that appears most often, rather than the number of distinct values.
You can re-fetch the starter code for this question: prac_q58.c.
Question 59Swap The First And Last Values
Estimated time: 15-20 minutes
Note prac_q59.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A rota is stored as a linked list and the first and last people on it need to trade places. Write a function that swaps the VALUES held in the first and last nodes and returns the head.
Lists with fewer than two nodes need no change.
Your task is to complete the function struct node *swap_ends(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q59.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q59.c:
gcc -Wall -Wextra -o prac_q59 prac_q59.c
./prac_q59
5
1 2 3 4 5
5 2 3 4 1
./prac_q59
2
7 8
8 7
./prac_q59
1
9
9Assumptions / Restrictions / Clarifications
- Swap the values, not the nodes; no relinking and no
malloc. - A list with fewer than two nodes is returned unchanged.
- Do not call
free. - Do not print anything inside
swap_ends.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- finding the last node
- swapping through a temporary variable
- short lists needing no work
Edge cases to consider
- a two-node list, where the two ends are adjacent
- a one-node list and an empty list, both unchanged
- a list where both ends already hold the same value
Common mistakes
- assigning
head->value = last->valuefirst, which loses the original and sets both to the same number - looping while
last != NULLand ending up withNULL - trying to swap the nodes themselves, which needs the node before the last one
Optional extension challenge
Swap the nodes themselves rather than their values, which is considerably harder.
You can re-fetch the starter code for this question: prac_q59.c.
Question 60Rotate A Linked List Forward
Estimated time: 20-25 minutes
Note prac_q60.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A rota rotates each week so the person at the front moves to the back. Write a function that moves the first node to the end of the list and returns the new head.
Lists with fewer than two nodes come back unchanged.
Your task is to complete the function struct node *rotate_forward(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q60.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q60.c:
gcc -Wall -Wextra -o prac_q60 prac_q60.c
./prac_q60
5
1 2 3 4 5
2 3 4 5 1
./prac_q60
2
7 8
8 7
./prac_q60
1
9
9Assumptions / Restrictions / Clarifications
- Move the existing node; do not call
mallocand do not callfree. - A list with fewer than two nodes is returned unchanged.
- The rotated list must still end in
NULL. - Do not print anything inside
rotate_forward.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- relinking three pointers in the right order
- finding the tail before changing anything
- why the old
headmust end inNULL
Edge cases to consider
- a two-node list, where the ends simply trade places
- a one-node list and an empty list, both unchanged
- checking the list still terminates and has not become a loop
Common mistakes
- forgetting
head->next = NULL, which leaves a cycle and makes printing loop forever - setting
last->nextbefore savinghead->next, losing the newhead - walking the tail search after the links have already been changed
Optional extension challenge
Rotate the list backward instead, moving the LAST node to the front.
You can re-fetch the starter code for this question: prac_q60.c.
Question 61Struct Array: Student Top Scorer
Estimated time: 20-30 minutes
Note prac_q61.c uses the following data type:
struct student {
char name[50];
int mark;
};A course convenor wants a quick way to find the top-performing student in a cohort based on their exam results. A struct student (see the provided starter header) stores a name and an integer mark out of 100. Write a function that, given the students array containing n students, returns the index of the student with the highest mark.
If multiple student records are tied for the highest mark, return the index of the first such student (i.e. the one with the smallest index) so the result is deterministic.
Your task is to complete the function int top_scorer(struct student students[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q61.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q61.c:
gcc -Wall -Wextra -o prac_q61 prac_q61.c
./prac_q61
3
Ada 88
Grace 95
Alan 95
1
./prac_q61
1
Katherine 91
0
./prac_q61
3
Bob 60
Cy 72
Dee 99
2Assumptions / Restrictions / Clarifications
- You may assume
nis greater than 0; the function is never called on an empty array. - Marks are integers between 0 and 100 inclusive.
- If several student records share the highest mark, the earliest one in the array (smallest index) must be returned.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- arrays of structs
- tracking the best record while scanning
- returning a record or an index
Worked example
For example:
For students containing Ada 88, Grace 95, Alan 95:
- best starts at index 0 (Ada, 88).
- index 1 (Grace, 95) has a higher mark than 88, so best becomes 1.
- index 2 (Alan, 95) is not strictly greater than 95, so best stays 1.
top_scorer returns 1, the first student to reach the highest mark.
Edge cases to consider
- a single student
- two student records tied for the top score
- all student records having the same score
- scores with large differences
Common mistakes
- comparing the wrong field (name vs score)
- returning a copy that goes stale, or an index that is off by one
- not defining tie-break behaviour, so results are non-deterministic
Optional extension challenge
Return the top k students (by score) sorted descending, not just the single best.
You can re-fetch the starter code for this question: prac_q61.c.
Question 62Anagram Checker
Estimated time: 25-35 minutes
A word game wants to let players check whether the letters they have rearranged form a valid anagram of a target word. Write a function that determines whether two null-terminated strings consisting only of lowercase letters are anagrams of each other (i.e. one is a rearrangement of the letters of the other), returning 1 if so and 0 otherwise.
Both strings must use exactly the same multiset of letters, meaning each letter must appear the same number of times in both strings, not just the same set of distinct letters.
Your task is to complete the function int is_anagram(char *s1, char *s2), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q62.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q62.c:
gcc -Wall -Wextra -o prac_q62 prac_q62.c
./prac_q62
listen
silent
1
./prac_q62
hello
world
0
./prac_q62
aabbcc
abcabc
1Assumptions / Restrictions / Clarifications
- Both strings contain only lowercase letters
'a'-'z'(no spaces or punctuation). - Strings of different lengths can never be anagrams of each other.
- Repeated letters matter:
"aab"and"abb"are not anagrams of each other.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- character frequency counting
- fixed-size tally arrays
- comparing two strings structurally
Worked example
For example:
For s1 = "listen" and s2 = "silent":
- counting each letter of "listen" gives +1 to l, i, s, t, e, n.
- counting each letter of "silent" subtracts 1 from s, i, l, e, n, t.
- every letter's net count is 0.
is_anagram returns 1.
Edge cases to consider
- two empty strings (trivially anagrams)
- strings of different lengths (cannot be anagrams)
- case differences and whether they matter
- repeated letters that must be counted, not just matched once
Common mistakes
- comparing sorted copies but mutating the caller's strings
- using a 26-slot table but feeding it non-letters out of range
- declaring equal after matching only the letters present in one string
Optional extension challenge
Ignore spaces and punctuation and treat upper/lowercase as equal so that "Dormitory" and "Dirty room" match.
You can re-fetch the starter code for this question: prac_q62.c.
Question 63Run-Length Encode
Estimated time: 25-35 minutes
A basic image/fax compression scheme reduces long runs of the same symbol to a shorter representation. Write a function that performs simple run-length encoding of a null-terminated string made up of uppercase letters only, writing the encoded result into a buffer supplied by the caller.
Each maximal run of a repeated character c of length k is written as the character c followed by the decimal digits of k. The buffer is guaranteed to be large enough to hold the result plus a null terminator.
Your task is to complete the function void run_length_encode(char *input, char *output), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q63.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q63.c:
gcc -Wall -Wextra -o prac_q63 prac_q63.c
./prac_q63
AAABCCDD
A3B1C2D2
./prac_q63
A
A1
./prac_q63
ABCD
A1B1C1D1
./prac_q63
Assumptions / Restrictions / Clarifications
inputcontains only uppercase letters'A'-'Z'and is null-terminated.- A run of length 1 is still written with the digit
'1'(e.g."A"encodes as"A1"). outputis a caller-supplied buffer guaranteed to be large enough to hold the encoded result plus a null terminator; you must null-terminateoutput.inputmay be the empty string, in which caseoutputshould also be the empty string.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- scanning for runs of equal characters
- building the string in the
outputbuffer - number-to-text formatting
Worked example
For example:
For input = "AAABCCDD":
- the run of
'A'has length 3, written as"A3". - the run of
'B'has length 1, written as"B1". - the run of
'C'has length 2, written as"C2". - the run of
'D'has length 2, written as"D2".
output is "A3B1C2D2".
Edge cases to consider
- an empty string in
input - a string with no repeats (every run length 1)
- a very long run (length crossing 9 into multiple digits)
- a single character
Common mistakes
- writing the run length before the character or vice versa inconsistently
- off-by-one in the run counter when the run ends at the string terminator
- forgetting to null-terminate the
outputbuffer
Optional extension challenge
Support decoding as well: turn "4a3b" back into "aaaabbb", validating the encoded form as you go.
You can re-fetch the starter code for this question: prac_q63.c.
Question 64Longest Common Prefix
Estimated time: 25-35 minutes
An autocomplete feature wants to display the common starting text shared by all the matching suggestions for what a user has typed so far. Write a function that finds the longest common prefix shared by all strings in an array of n null-terminated strings, writing it (null-terminated) into a buffer supplied by the caller.
If there is no common prefix, or n is 0, the buffer should contain the empty string.
Your task is to complete the function void longest_common_prefix(char *strs[], int n, char *result), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q64.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q64.c:
gcc -Wall -Wextra -o prac_q64 prac_q64.c
./prac_q64
3
flower
flow
flight
fl
./prac_q64
2
dog
cat
./prac_q64
2
test
test
testAssumptions / Restrictions / Clarifications
- The
resultbuffer is guaranteed to be large enough to hold the longest possible prefix plus a null terminator. - Comparison is case-sensitive.
nmay be 0, in which caseresultshould be set to the empty string.- If one of the strings is itself a prefix of all the others (e.g. the shortest string), the common prefix can be no longer than that string.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- comparing multiple strings column by column
- early termination
- copying a prefix out
Worked example
For example:
For strs = ["flower", "flow", "flight"]:
- position 0: all three strings have 'f' -- matches, prefix so far "f".
- position 1: all three have 'l' -- matches, prefix so far "fl".
- position 2: "flower" and "flow" have 'o', but "flight" has 'i' -- mismatch.
The function stops there, so result is "fl".
Edge cases to consider
- an array containing the empty string (prefix must be empty)
- all strings identical
- a single string (the whole string is the prefix)
- no common prefix at all
Common mistakes
- reading past the end of the shortest string
- comparing pairs instead of the same column across all strings
- forgetting to terminate the output prefix
Optional extension challenge
Return the longest common suffix instead, or find the pair of strings with the longest shared prefix.
You can re-fetch the starter code for this question: prac_q64.c.
Question 65Matrix Transpose
Estimated time: 25-35 minutes
A small linear-algebra library needs a matrix-transposition routine as part of larger computations. Write a function that transposes a rows x cols 2D array of integers, storing the result (a cols x rows array) into a second 2D array supplied by the caller.
Transposition swaps rows and columns, so input[i][j] becomes output[j][i]. For example, a 2x3 matrix becomes a 3x2 matrix after transposing.
Your task is to complete the function void transpose(int input[10][10], int output[10][10], int rows, int cols), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q65.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q65.c:
gcc -Wall -Wextra -o prac_q65 prac_q65.c
./prac_q65
2 3
1 2 3
4 5 6
1 4
2 5
3 6
./prac_q65
2 2
1 2
3 4
1 3
2 4
./prac_q65
1 3
5 6 7
5
6
7Assumptions / Restrictions / Clarifications
- You may assume
rowsandcolsare each at most 10, matching the fixed array size. inputandoutputare separate arrays; do not attempt the operation in place.- Only the first
rowsxcolsregion ofinputis meaningful; the rest may contain garbage and should be ignored. - Only the first
colsxrowsregion ofoutputneeds to be written.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- 2D array indexing
- mapping
[i][j]to[j][i] - distinguishing the
rowscount from the column count
Worked example
For example:
For the 2x3 matrix [[1,2,3],[4,5,6]]:
output[0][0] = input[0][0] = 1,output[0][1] = input[1][0] = 4.output[1][0] = input[0][1] = 2,output[1][1] = input[1][1] = 5.output[2][0] = input[0][2] = 3,output[2][1] = input[1][2] = 6.
The printed 3x2 result is "1 4", "2 5", "3 6".
Edge cases to consider
- a non-square matrix (dimensions swap)
- a 1xN or Nx1 matrix
- a 1x1 matrix
- a square matrix where an in-place operation is tempting
Common mistakes
- swapping in place across the whole matrix and undoing every swap
- mixing up the row and column bounds for a non-square matrix
- writing to
out[i][j]instead ofout[j][i]
Optional extension challenge
Transpose a square matrix in place (no second matrix) by swapping only the upper triangle.
You can re-fetch the starter code for this question: prac_q65.c.
Question 66Linked List Sum
Estimated time: 25-35 minutes
Note prac_q66.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A shopping cart is implemented internally as a singly linked list of item prices (as integer cents), and the checkout screen needs to display the running total. A singly linked list of integers is defined by the provided struct node. Write a function that returns the sum of all values stored in the list.
An empty list (head == NULL) has a sum of 0, representing an empty cart.
Your task is to complete the function int list_sum(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q66.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q66.c:
gcc -Wall -Wextra -o prac_q66 prac_q66.c
./prac_q66
4
3 8 1 5
17
./prac_q66
0
0
./prac_q66
1
42
42Assumptions / Restrictions / Clarifications
- Do not modify the list; the traversal should be read-only.
- The list may be arbitrarily long.
- Values stored in the list may be negative, zero, or positive.
headmay beNULL, representing an empty list.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- linked list traversal
- following next pointers to
NULL - accumulating over nodes
Worked example
For example:
For the list 3 -> 8 -> 1 -> 5 -> NULL:
- sum starts at 0.
- visiting each node in turn adds 3, then 8, then 1, then 5: 0+3=3, 3+8=11, 11+1=12, 12+5=17.
list_sum returns 17.
Edge cases to consider
- an empty list (
head == NULL), sum 0 - a single-node list
- a list containing negative data values
- a long list
Common mistakes
- dereferencing a
NULLheadbefore checking it - stopping one node early or looping forever by not advancing the cursor
- modifying the list while summing it
Optional extension challenge
Also return the number of nodes and compute the average, all in a single traversal.
You can re-fetch the starter code for this question: prac_q66.c.
Question 67Merge Two Sorted Arrays
Estimated time: 25-35 minutes
Two separate leaderboards have each been sorted in ascending order of score, and you need to combine them into a single sorted leaderboard without re-sorting everything from scratch. Write a function that merges two arrays of integers, each already sorted in ascending order, into a single sorted array.
The caller supplies a result array guaranteed to be large enough to hold n1 + n2 elements. Your function should run in a single linear pass over both input arrays, taking advantage of the fact that they are already sorted, rather than concatenating and re-sorting.
Your task is to complete the function void merge_sorted(int a[], int n1, int b[], int n2, int result[]), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q67.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q67.c:
gcc -Wall -Wextra -o prac_q67 prac_q67.c
./prac_q67
3
1 4 7
4
2 3 8 9
1 2 3 4 7 8 9
./prac_q67
2
5 6
0
5 6
./prac_q67
3
2 2 5
2
2 6
2 2 2 5 6Assumptions / Restrictions / Clarifications
- Both
aandbare guaranteed to already be sorted in ascending order. - Either array may be empty (
n1orn2equal to 0). - Duplicate values, whether within one array or across the two arrays, should all be kept in
result. resultis guaranteed to be large enough to holdn1 + n2elements.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- two-pointer merge
- preserving sorted order
- writing into an output array
Worked example
For example:
For a = [1, 4, 7] and b = [2, 3, 8, 9]:
- compare 1 and 2: 1 is smaller, take 1.
- compare 4 and 2: 2 is smaller, take 2.
- compare 4 and 3: 3 is smaller, take 3.
- compare 4 and 8: 4 is smaller, take 4.
- compare 7 and 8: 7 is smaller, take 7.
ais now exhausted, so the remaining elements ofb(8, 9) are appended.
result is "1 2 3 4 7 8 9".
Edge cases to consider
- one array empty and the other not
- both arrays empty
- arrays with duplicate values across both inputs
- all elements of one array smaller than the other
Common mistakes
- forgetting to copy the leftover tail of the longer array
- advancing both indices when only one element was consumed
- writing past the end of the output array
Optional extension challenge
Merge the two arrays in place into the first one, assuming it has enough spare capacity at the end.
You can re-fetch the starter code for this question: prac_q67.c.
Question 68Rotate Array Left
Estimated time: 25-35 minutes
A circular buffer used in a music player's queue of upcoming tracks needs to be rotated whenever the currently playing track changes, so that the queue always starts from the correct position. Write a function that rotates the elements of an array of n integers to the left by k positions in place, so that the element originally at index k becomes the new element at index 0, wrapping around.
You must not allocate a second array the same size as arr to perform the rotation; the array should be updated in place.
Your task is to complete the function void rotate_left(int arr[], int n, int k), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q68.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q68.c:
gcc -Wall -Wextra -o prac_q68 prac_q68.c
./prac_q68
5
1 2 3 4 5
2
3 4 5 1 2
./prac_q68
3
1 2 3
7
2 3 1
./prac_q68
3
9 8 7
0
9 8 7Assumptions / Restrictions / Clarifications
0 <= k, andkmay be larger thann(in which case reduce it withk % n).nmay be 0, in which case the function does nothing.kmay be 0, in which case the array is left unchanged.- The relative order of elements is preserved; only their starting position changes.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- modular index arithmetic
- rotation by
kpositions - handling
klarger thann
Worked example
For example:
For arr = [1, 2, 3, 4, 5] and k = 2:
- the element originally at index 2 (value 3) becomes the new index 0.
- reading two positions ahead (wrapping around) for every index gives [3, 4, 5, 1, 2].
The array printed by main is "3 4 5 1 2".
Edge cases to consider
k == 0(no change)kequal to or a multiple ofn(no change)kgreater thann(reduce with %n)- an empty or single-element array
Common mistakes
- not reducing
kmodulon, causing out-of-bounds indexing - rotating right instead of left
- overwriting elements before they have been copied when done naively
Optional extension challenge
Rotate using the three-reversal trick (reverse parts, then the whole) to achieve O(1) extra space.
You can re-fetch the starter code for this question: prac_q68.c.
Question 69Caesar Cipher
Estimated time: 20-30 minutes
A retro cryptography demo wants to reproduce one of the oldest known ciphers, in which every letter of a message is shifted a fixed number of positions through the alphabet. Write a function that applies a Caesar cipher shift to a null-terminated string consisting only of lowercase letters, shifting every letter forward in the alphabet by shift positions, wrapping around from 'z' back to 'a'.
The string should be modified in place; the function does not return a new string or allocate any memory.
Your task is to complete the function void caesar_shift(char *s, int shift), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q69.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q69.c:
gcc -Wall -Wextra -o prac_q69 prac_q69.c
./prac_q69
hello
3
khoor
./prac_q69
xyz
3
abc
./prac_q69
abc
0
abcAssumptions / Restrictions / Clarifications
0 <= shift && shift <= 25.- The string contains only lowercase letters
'a'-'z'(no spaces or punctuation). - Shifting must wrap around correctly, e.g.
'z'shifted by 1 becomes'a'. - A
shiftof 0 should leave the string unchanged.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- character arithmetic on letters
- modular wrap-around within lowercase letters
- applying uniform transformations to text
Worked example
For example:
For s = "xyz" and shift = 3:
'x'is 23 positions after'a';(23 + 3) % 26 = 0, giving'a'.'y'is 24 positions after'a';(24 + 3) % 26 = 1, giving'b'.'z'is 25 positions after'a';(25 + 3) % 26 = 2, giving'c'.
s becomes "abc", demonstrating the wrap-around from 'z' back to 'a'.
Edge cases to consider
- a
shiftof 0 (unchanged text) - a
shiftof 25 (maximum allowedshift) - wrapping past
zback toa - strings with repeated identical characters
Common mistakes
- applying
% 26to the raw ASCII code instead of the letter offset - off-by-one in the alphabet boundary check
- forgetting to null-terminate or preserve string length
Optional extension challenge
Write the decryption function caesar_decrypt(char *s, int shift) that reverses the transformation.
You can re-fetch the starter code for this question: prac_q69.c.
Question 70Struct Array: Total Inventory Value
Estimated time: 25-35 minutes
Note prac_q70.c uses the following data type:
struct item {
char name[50];
int quantity;
double unit_price;
};A small warehouse management system needs to report the total dollar value of everything currently in stock. A struct item (see the provided starter header) stores a name, an integer quantity, and a double unit_price.
Write a function that computes the total value of an inventory (an items array containing n records), summing quantity * unit_price across all records.
Your task is to complete the function double total_value(struct item items[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q70.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q70.c:
gcc -Wall -Wextra -o prac_q70 prac_q70.c
./prac_q70
2
Widget 10 2.50
Gadget 3 15.00
70
./prac_q70
0
0
./prac_q70
1
OutOfStock 0 9.99
0Assumptions / Restrictions / Clarifications
nmay be 0, in which case the function returns 0.0.quantityis always non-negative;unit_priceis always non-negative.- The result should be returned as a
doubleto correctly represent cents.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- arrays of structs
- accumulating a floating-point total
- quantity times price per item
Worked example
For example:
For items containing Widget (qty 10, $2.50) and Gadget (qty 3, $15.00):
- Widget contributes 10 * 2.50 = 25.0.
- Gadget contributes 3 * 15.00 = 45.0.
- 25.0 + 45.0 = 70.0.
total_value returns 70.0.
Edge cases to consider
- an empty inventory (total 0.0)
- an item with quantity 0
- a single item
- prices that accumulate floating-point rounding
Common mistakes
- summing price without multiplying by quantity
- using
intfor the running total and truncating cents - iterating past the number of items actually present
Optional extension challenge
Also return the single most valuable line item (quantity times price) via an output parameter.
You can re-fetch the starter code for this question: prac_q70.c.
Question 71Bubble Sort Descending
Estimated time: 25-35 minutes
As part of an algorithms unit, you are asked to implement a classic sorting algorithm by hand rather than relying on a library function. Write a function that sorts an array of n integers into descending order in place, using the bubble sort algorithm (repeatedly stepping through the array, swapping adjacent elements that are in the wrong order).
The sort must continue making passes over the array until no more swaps are needed, at which point the array is fully sorted in descending order.
Your task is to complete the function void bubble_sort_desc(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q71.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q71.c:
gcc -Wall -Wextra -o prac_q71 prac_q71.c
./prac_q71
8
3 1 4 1 5 9 2 6
9 6 5 4 3 2 1 1
./prac_q71
3
5 3 1
5 3 1
./prac_q71
1
7
7Assumptions / Restrictions / Clarifications
- You must implement bubble sort specifically (repeated adjacent swaps); do not call a library sort function such as
qsort. nmay be 0 or 1, in which case the array is already trivially sorted.arrmay contain duplicate values, which should remain adjacent to each other after sorting.- The sort must be performed in place, without allocating a second array.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- nested-loop sorting
- adjacent swaps
- descending vs ascending comparison direction
Worked example
For example:
For arr = [3, 1, 4, 1, 5, 9, 2, 6]:
- each pass compares adjacent elements and swaps them if the left one is smaller than the right one.
- after enough passes, larger values 'bubble' to the front and smaller values sink to the back.
- once a full pass makes no swaps, the array is fully sorted.
The final descending order is "9 6 5 4 3 2 1 1".
Edge cases to consider
- an already-sorted (descending) array
- an array sorted the wrong way (fully reversed)
- an array with duplicate values
- a single-element or empty array
Common mistakes
- using the ascending comparison and sorting the wrong way
- off-by-one in the inner loop bound reading past the end
- swapping indices rather than the values at those indices
Optional extension challenge
Add an early-exit optimisation that stops once a full pass makes no swaps, and count the swaps performed.
You can re-fetch the starter code for this question: prac_q71.c.
Question 72Remove Duplicates From Sorted Array
Estimated time: 25-35 minutes
A sensor logs readings into a sorted array over time, but occasionally records the same reading twice in a row and you need to clean this up in place without using extra memory. Write a function that removes duplicate values from an array of n integers that is already sorted in ascending order, compacting the unique values to the front of the array (in order) and returning the count of unique values.
The contents of the array beyond the returned count are unspecified and will not be checked; only arr[0] up to arr[returned_count - 1] need to be correct.
Your task is to complete the function int remove_duplicates(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q72.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q72.c:
gcc -Wall -Wextra -o prac_q72 prac_q72.c
./prac_q72
7
1 1 2 3 3 3 5
4
1 2 3 5
./prac_q72
3
2 4 6
3
2 4 6
./prac_q72
0
0Assumptions / Restrictions / Clarifications
nmay be 0, in which case the function should return 0.- The array is guaranteed to be sorted ascending on entry, so duplicates are always adjacent.
- You must modify
arrin place; do not allocate a second array. - The relative order of the unique values must be preserved.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- in-place compaction with a write index
- exploiting sorted order
- returning the new length
Worked example
For example:
For arr = [1, 1, 2, 3, 3, 3, 5]:
writestarts at 1 (arr[0] = 1is always kept).read = 1:arr[1] = 1equalsarr[write - 1] = 1, skip.read = 2:arr[2] = 2differs from 1, write it at index 1, sowritebecomes 2.read = 3, 4, 5: 3 is written once (index 2), and the two further 3s are skipped.read = 6: 5 differs from 3, is written at index 3, andwritebecomes 4.
remove_duplicates returns 4, and arr[0..3] is "1 2 3 5".
Edge cases to consider
- an empty array (new length 0)
- an array with no duplicates
- an array where every element is identical (new length 1)
- duplicates only at the very end
Common mistakes
- using a second array instead of compacting in place
- comparing non-adjacent elements and missing runs
- returning the old length or an off-by-one new length
Optional extension challenge
Instead of keeping one copy, keep only elements that appear exactly once, removing all duplicated values entirely.
You can re-fetch the starter code for this question: prac_q72.c.
Question 73Balanced Brackets
Estimated time: 25-35 minutes
A code editor's syntax highlighter needs to check whether the brackets in a snippet of source code are correctly matched before attempting to parse it further. Write a function that determines whether the brackets in a null-terminated string are balanced.
The string may contain the bracket characters '(', ')', '[', ']', '{', '}' interspersed with other characters, which should be ignored. Brackets are balanced if every opening bracket has a matching closing bracket of the same type, correctly nested, in the correct order. Return 1 if balanced, 0 otherwise. You should implement this using an array as a stack (no library stack/queue is provided).
Your task is to complete the function int is_balanced(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q73.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q73.c:
gcc -Wall -Wextra -o prac_q73 prac_q73.c
./prac_q73
a(b[c]d)e
1
./prac_q73
(a[b)c]
0
./prac_q73
{[()]}
1
./prac_q73
(
0Assumptions / Restrictions / Clarifications
- You may assume the string is at most 999 characters long.
- Non-bracket characters (letters, digits, punctuation) do not affect the result.
- Brackets of different types must nest correctly (e.g.
"(]"is not balanced even though each bracket type individually appears once). - The empty string is considered balanced.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- stack-based matching
- pushing openers and popping on closers
- detecting mismatches and leftovers
Worked example
For example:
For s = "(a[b)c]":
'('is pushed. stack:('a'is ignored.'['is pushed. stack:( ['b'is ignored.')'is seen, so the top of the stack is popped: it is'[', not'(', which is a mismatch.
is_balanced returns 0 as soon as this mismatch is detected.
Edge cases to consider
- the empty string (balanced)
- a closing bracket with nothing open (immediate failure)
- correct counts but wrong nesting order, e.g.
"([)]" - unclosed openers left on the stack at the end
Common mistakes
- only counting brackets instead of checking nesting order
- forgetting to fail when the stack is empty on a closer
- declaring balanced while openers remain unmatched at the end
Optional extension challenge
Support three bracket kinds ((), [], {}) and report the index at which the first mismatch occurs.
You can re-fetch the starter code for this question: prac_q73.c.
Question 74Compress Whitespace
Estimated time: 25-35 minutes
A form-submission handler wants to tidy up freeform text fields that users have typed messily, with stray leading, trailing, or doubled-up spaces. Write a function that collapses every run of one or more consecutive space characters in a null-terminated string into a single space, and removes any leading or trailing spaces, modifying the string in place.
Because the resulting string is always the same length as or shorter than the original, this can be done in place without needing a second buffer.
Your task is to complete the function void compress_whitespace(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q74.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q74.c:
gcc -Wall -Wextra -o prac_q74 prac_q74.c
./prac_q74
hello world
hello world
./prac_q74
already fine
already fine
./prac_q74
Assumptions / Restrictions / Clarifications
- Only the space character
' 'is treated as whitespace (not tabs or newlines). - The resulting string is always no longer than the original.
- A string that is empty, or contains only spaces, becomes the empty string after compressing.
- The string must be modified in place; no new buffer is allocated or returned.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- in-place string rewriting
- collapsing runs of spaces
- trimming leading/trailing spaces
Worked example
For example:
For s = " hello world ":
- the two leading spaces are skipped entirely.
- "hello" is copied across unchanged.
- the run of three spaces is collapsed to a single space.
- "world" is copied across unchanged.
- the two trailing spaces are removed at the end.
s becomes "hello world".
Edge cases to consider
- the empty string
- a string that is all spaces (becomes empty)
- leading and trailing spaces to trim
- multiple internal runs of spaces
Common mistakes
- leaving a single leading or trailing space behind
- collapsing runs but forgetting to re-terminate the shortened string
- using a read index and write index that get out of sync
Optional extension challenge
Also collapse tabs and newlines into single spaces, and normalise any run of mixed whitespace to one space.
You can re-fetch the starter code for this question: prac_q74.c.
Question 75Grid Neighbour Sum
Estimated time: 25-35 minutes
A cellular-automaton simulation (in the spirit of Conway's Game of Life) needs to compute a value for each cell based on its immediate neighbours before updating the grid. Given a rows x cols 2D array of integers representing a grid, write a function that computes, for a given cell (r, c), the sum of the values of its orthogonal neighbours (up, down, left, right) that lie within the grid bounds.
Neighbours that fall outside the grid (for cells on an edge or corner) are simply not included in the sum, rather than being treated as zero or causing an error.
Your task is to complete the function int neighbour_sum(int grid[10][10], int rows, int cols, int r, int c), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q75.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q75.c:
gcc -Wall -Wextra -o prac_q75 prac_q75.c
./prac_q75
3 3
1 2 3
4 5 6
7 8 9
1 1
20
./prac_q75
3 3
1 2 3
4 5 6
7 8 9
0 0
6
./prac_q75
3 3
1 2 3
4 5 6
7 8 9
0 1
9Assumptions / Restrictions / Clarifications
0 <= r && r < rowsand0 <= c && c < cols.rowsandcolsare each at most 10, matching the fixed array size.- Diagonal neighbours are not included, only up/down/left/right.
- Corner and edge cells have fewer than 4 in-bounds neighbours; only the ones that exist should be summed.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- 2D neighbourhood iteration
- boundary/edge handling
- summing the four orthogonal neighbours
Worked example
For example:
For grid = [[1,2,3],[4,5,6],[7,8,9]] and cell (1,1) (the centre, value 5):
- up neighbour (0,1) = 2.
- down neighbour (2,1) = 8.
- left neighbour (1,0) = 4.
- right neighbour (1,2) = 6.
- all four neighbours are in bounds: 2 + 8 + 4 + 6 = 20.
neighbour_sum returns 20.
Edge cases to consider
- a corner cell (only two orthogonal neighbours)
- an edge cell (only three orthogonal neighbours)
- a 1x1
grid(no neighbours) - excluding the cell itself from its own neighbour sum
Common mistakes
- reading outside the
gridat edges and corners - including the centre cell in its own sum
- using signed offsets without clamping to valid indices
Optional extension challenge
Compute one Game of Life step for the whole grid, deriving each new cell from its neighbour count.
You can re-fetch the starter code for this question: prac_q75.c.
Question 76Second Largest in Array
Estimated time: 20-30 minutes
A leaderboard wants to display not just the winner but also the runner-up score. Write a function that returns the second largest DISTINCT value in an array of n integers.
If every element is equal (so there is no distinct second value), return that single value.
Your task is to complete the function int second_largest(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q76.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q76.c:
gcc -Wall -Wextra -o prac_q76 prac_q76.c
./prac_q76
8
3 1 4 1 5 9 2 6
6
./prac_q76
3
7 7 7
7
./prac_q76
2
10 20
10Assumptions / Restrictions / Clarifications
- You may assume
n>= 2. - Duplicates of the maximum do not count as the second largest; you want the largest value strictly less than the maximum.
- If all elements are equal, return that value.
- Do not modify the input array and do not print inside
second_largest.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- two passes over an array
- handling duplicate maxima
- a found/sentinel flag
Worked example
For example:
For the array 3 1 4 1 5 9 2 6:
- the maximum value is 9.
- the largest value strictly less than 9 is 6.
second_largesttherefore returns 6.
Edge cases to consider
- all elements equal (return that value)
- several copies of the maximum
- exactly two elements
- negative values
Common mistakes
- returning the second position after a naive sort rather than the second distinct value
- initialising second to 0, which breaks for all-negative arrays
- counting a duplicate of the maximum as the second largest
Optional extension challenge
Return the k-th largest distinct value for a k passed as a parameter, without fully sorting the array.
You can re-fetch the starter code for this question: prac_q76.c.
Question 77Debug and Fix: Array Sum
Estimated time: 15-25 minutes
The starter file for this question contains a function that is SUPPOSED to return the sum of the first n elements of an array, but it is deliberately broken: it contains two bugs. Your task is to find and fix them so the function returns the correct sum.
This is a debugging exercise. Study the given implementation, work out why its output is wrong, and repair it. Do not rewrite the function from scratch unless you need to; the goal is to understand the specific mistakes.
The two bugs to look for are: the accumulator is initialised incorrectly, and the loop bounds are off by one (it skips the first element and reads one element past the end of the meaningful data).
Your task is to complete the function int sum_array(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q77.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q77.c:
gcc -Wall -Wextra -o prac_q77 prac_q77.c
./prac_q77
4
1 2 3 4
10
./prac_q77
2
-5 5
0
./prac_q77
0
0Assumptions / Restrictions / Clarifications
- After your fix, the function must return 0 when
nis 0. - The corrected function must read exactly the elements
arr[0]througharr[n - 1]. - Do not change the function's prototype or the main function.
- Do not print anything inside
sum_array.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- reading and understanding unfamiliar code
- spotting off-by-one loop errors
- accumulator initialisation
Worked example
For example:
For the array 1 2 3 4 (after fixing):
totalstarts at 0 and each ofarr[0..3]is added:1 + 2 + 3 + 4.- the correct sum is 10, so the fixed
sum_arrayreturns 10.
The unfixed starter would instead start at 1 and skip arr[0] while reading past the end, giving a wrong answer.
Edge cases to consider
n == 0, which must return 0 not 1- a single-element array
- arrays whose values cancel to 0
- negative values
Common mistakes
- fixing only one of the two bugs
- leaving total initialised to 1
- changing
i <= ntoi < nbut forgetting to also startiat 0
Optional extension challenge
Turn the corrected code into a function that returns both the sum and the count of positive elements via output parameters.
You can re-fetch the starter code for this question: prac_q77.c.
Question 78Traffic Light Controller
Estimated time: 15-20 minutes
Note prac_q78.c uses the following data type:
typedef enum {
RED,
GREEN,
YELLOW
} TrafficLight;A pedestrian-crossing controller advances a traffic light through its cycle. Write a function that, given the current light, returns the next light in the sequence RED then GREEN then YELLOW and back to RED.
The TrafficLight type is defined with a typedef enum, so you can use it directly as TrafficLight.
Your task is to complete the function TrafficLight next_light(TrafficLight current), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q78.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q78.c:
gcc -Wall -Wextra -o prac_q78 prac_q78.c
./prac_q78
0 1
GREEN
./prac_q78
0 3
RED
./prac_q78
1 2
REDAssumptions / Restrictions / Clarifications
- The only valid values are
RED,GREENandYELLOW. - After
YELLOWthe cycle returns toRED. - Return a
TrafficLightvalue; do not print insidenext_light.
Stuck? Here's a hint
What this practises
This question gives you practice with:
typedef enumtypesswitchstatements over enum values- cyclic state transitions
Worked example
For example:
For starting light 1 (GREEN) and 2 steps:
next_light(GREEN)returnsYELLOW.next_light(YELLOW)returnsRED.- after two steps the light is
RED, so the program printsRED.
Edge cases to consider
- advancing from YELLOW, which wraps to RED
- zero steps (light unchanged)
- three steps returning to the start
- a step count that is a multiple of three
Common mistakes
- falling through
switchcases by omittingreturn/break - hard-coding integer arithmetic that produces an out-of-range value
- forgetting the wrap from YELLOW back to RED
Optional extension challenge
Add a flashing-amber fault mode as a fourth state and a function that decides when to enter or leave it.
You can re-fetch the starter code for this question: prac_q78.c.
Question 79Sum Every k-th Element (Pointer Arithmetic)
Estimated time: 20-30 minutes
A sampling routine wants to sum every k-th reading from a data buffer, starting from the first. Write a function that returns the sum of the elements at indices 0, k, 2k, 3k, ..., up to (but not including) index n.
You must walk the array using POINTER ARITHMETIC (advancing a pointer by k each step) rather than indexing with square brackets.
Your task is to complete the function int sum_stride(int *arr, int n, int k), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q79.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q79.c:
gcc -Wall -Wextra -o prac_q79 prac_q79.c
./prac_q79
5
10 20 30 40 50
2
90
./prac_q79
3
5 5 5
1
15
./prac_q79
7
1 2 3 4 5 6 7
3
12Assumptions / Restrictions / Clarifications
- You may assume
k >= 1andn >= 0. - The first element (index 0) is always included when
n > 0. - Walk the array with a pointer that you advance by
keach iteration; do not usearr[i]indexing. - Do not print anything inside
sum_stride.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- pointer arithmetic
- computing a one-past-the-end pointer
- loop termination with pointers
Worked example
For example:
For k = 2 over the array 10 20 30 40 50:
- a pointer starts at index 0 (value 10) and advances by 2 each step.
- it visits indices 0, 2, 4, reading 10, 30, 50.
- the sum is 90, so
sum_stridereturns 90.
Edge cases to consider
k == 1(sum the whole array)klarger thann(only the first element counts)n == 0(empty, sum 0)- a
kthat lands the pointer exactly on the end
Common mistakes
- advancing the pointer by 1 instead of
k - using
p <= endand reading one element past the array - mixing index arithmetic and pointer arithmetic inconsistently
Optional extension challenge
Add an offset parameter so the walk can start at any index, and support negative strides that walk backwards.
You can re-fetch the starter code for this question: prac_q79.c.
Question 80Column Sums of a Matrix
Estimated time: 20-30 minutes
A spreadsheet feature needs a totals row that sums each column of a grid of numbers. Write a function that computes the sum of each column of a rows x cols matrix, storing the cols totals into an output array supplied by the caller.
Your task is to complete the function void column_sums(int matrix[10][10], int rows, int cols, int out[10]), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q80.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q80.c:
gcc -Wall -Wextra -o prac_q80 prac_q80.c
./prac_q80
2 3
1 2 3
4 5 6
5 7 9
./prac_q80
3 2
1 1
2 2
3 3
6 6
./prac_q80
1 4
10 20 30 40
10 20 30 40Assumptions / Restrictions / Clarifications
- You may assume
rowsandcolsare each at most 10, matching the fixed array size. out[c]should receive the sum of column c across allrows.- Only the first
rowsxcolsregion ofmatrixis meaningful. - Do not print anything inside
column_sums.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- 2D array traversal
- iterating column-major
- writing results into an output array
Worked example
For example:
For the 2x3 matrix [[1,2,3],[4,5,6]]:
- column 0 sums 1 + 4 = 5.
- column 1 sums 2 + 5 = 7.
- column 2 sums 3 + 6 = 9.
The printed totals are "5 7 9".
Edge cases to consider
- a single-row
matrix(totals equal that row) - a single-column
matrix - a 1x1
matrix rowsandcolsdiffering (non-square)
Common mistakes
- swapping the row and column loop bounds
- accumulating into
out[c]without resetting to 0 - iterating row-major and computing row sums by mistake
Optional extension challenge
Also compute the row sums and the grand total, returning them alongside the column sums.
You can re-fetch the starter code for this question: prac_q80.c.
Question 81Parse Integer With Validation
Estimated time: 25-35 minutes
A configuration loader must reject fields that are not proper integers instead of silently treating them as zero. This is a defensive-programming exercise. Write a function that parses a string as a signed decimal integer, returning 1 and storing the value via an output pointer if the whole string is a valid integer, or returning 0 (and leaving the output untouched) if it is not.
A valid integer is an optional single leading '-' followed by one or more decimal digits and nothing else. Empty strings, a lone '-', and strings with any non-digit character are invalid.
Your task is to complete the function int parse_int(char *s, int *out), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q81.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q81.c:
gcc -Wall -Wextra -o prac_q81 prac_q81.c
./prac_q81
-34
-34
./prac_q81
12a
invalid
./prac_q81
007
7
./prac_q81
invalid
./prac_q81
-
invalidAssumptions / Restrictions / Clarifications
- The empty string is invalid.
- A leading '-' is allowed but must be followed by at least one digit.
- No leading '+', spaces, or trailing characters are permitted.
- On success, write the parsed value through
outand return 1; on failure, return 0 and do not write throughout. - You may assume any valid value fits in an
int.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- string validation / defensive programming
- manual digit-to-value conversion
- output parameters with a success flag
Worked example
For example:
For the string "12a":
- the first two characters '1' and '2' are digits and build the value 12.
- the character 'a' is not a digit, so the function returns 0 without writing through
out.
main prints "invalid".
Edge cases to consider
- the empty string (invalid)
- a lone '-' with no digits (invalid)
- leading zeros like "007" (valid, value 7)
- a trailing non-digit like "12a"
Common mistakes
- accepting a '-' with no following digits
- writing through
outeven on failure - treating the empty string as 0 instead of invalid
Optional extension challenge
Extend it to accept an optional leading '+' and to reject values that would overflow an int.
You can re-fetch the starter code for this question: prac_q81.c.
Question 82Most Frequent Element
Estimated time: 20-30 minutes
A poll aggregator wants the single most popular choice from a list of votes. Write a function that returns the value that occurs most often in an array of n integers. If two or more values tie for the highest frequency, return the smallest such value.
Your task is to complete the function int most_frequent(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q82.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q82.c:
gcc -Wall -Wextra -o prac_q82 prac_q82.c
./prac_q82
6
1 2 2 3 3 3
3
./prac_q82
4
4 4 5 5
4
./prac_q82
1
7
7Assumptions / Restrictions / Clarifications
- You may assume
n >= 1. - If several values share the highest frequency, return the smallest of them.
- Do not modify the input array.
- Do not print anything inside
most_frequent.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- nested-loop frequency counting
- tracking a best-so-far value
- deterministic tie-breaking
Worked example
For example:
For the array 4 4 5 5:
- 4 occurs twice and 5 occurs twice, a tie for the highest frequency.
- the tie is broken by returning the smaller value, 4.
most_frequenttherefore returns 4.
Edge cases to consider
- a single-element array
- all elements distinct (each frequency 1, smallest wins)
- all elements equal
- a tie broken by the smallest value
Common mistakes
- not defining a tie-break, giving non-deterministic results
- comparing counts but forgetting to update
best_value - initialising
best_countto a value that the first element cannot beat
Optional extension challenge
Solve it in O(n log n) by sorting a copy first, or discuss how a hash table would make it O(n).
You can re-fetch the starter code for this question: prac_q82.c.
Question 83Titlecase Words
Estimated time: 20-30 minutes
A headline formatter needs to convert arbitrary text to title case, where the first letter of each word is uppercase and every other letter is lowercase. Write a function that converts a string to title case IN PLACE.
Words are separated by single spaces. Only alphabetic characters are changed; the first alphabetic character of each word becomes uppercase and the rest become lowercase.
Your task is to complete the function void titlecase(char *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q83.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q83.c:
gcc -Wall -Wextra -o prac_q83 prac_q83.c
./prac_q83
hello world
Hello World
./prac_q83
the QUICK brown FOX
The Quick Brown Fox
./prac_q83
a
AAssumptions / Restrictions / Clarifications
- Words are separated by single space characters.
- The first letter of each word becomes uppercase; all other letters become lowercase.
- The string is modified in place; do not allocate a new string.
- You may use functions from
<ctype.h>such astoupperandtolower.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- in-place string modification
- tracking word boundaries
toupper/tolowerfrom<ctype.h>
Worked example
For example:
For the string "the QUICK brown FOX":
- each word's first letter is forced uppercase: 'T', 'Q', 'B', 'F'.
- every following letter is forced lowercase.
- the result is "The Quick Brown Fox".
Edge cases to consider
- the empty string
- a single-character word
- already-correct title case
- all-uppercase input that must be lowered except first letters
Common mistakes
- only uppercasing and forgetting to lowercase the rest of each word
- not resetting the start-of-word flag after a space
- passing a negative
chartotoupperwithout theunsigned charcast
Optional extension challenge
Handle multiple spaces and other separators, and keep small words like 'a', 'of', 'the' lowercase unless first.
You can re-fetch the starter code for this question: prac_q83.c.
Question 84Count Distinct Values
Estimated time: 20-30 minutes
An analytics module wants to know how many unique readings a sensor produced. Write a function that returns the number of distinct values in an array of n integers, without modifying the array.
Your task is to complete the function int count_distinct(int arr[], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q84.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q84.c:
gcc -Wall -Wextra -o prac_q84 prac_q84.c
./prac_q84
5
1 2 2 3 1
3
./prac_q84
3
5 5 5
1
./prac_q84
0
0Assumptions / Restrictions / Clarifications
- If
nis 0 the function returns 0. - Each value should be counted once no matter how many times it appears.
- Do not modify or sort the input array.
- Do not print anything inside
count_distinct.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- nested-loop uniqueness check
- looking back over already-seen elements
- not mutating the input
Worked example
For example:
For the array 1 2 2 3 1:
- 1 is new (counts), 2 is new (counts), the second 2 was seen before (skip).
- 3 is new (counts), the second 1 was seen before (skip).
- three distinct values remain, so
count_distinctreturns 3.
Edge cases to consider
- empty array (0 distinct)
- all elements equal (1 distinct)
- all elements distinct (
ndistinct) - negative values
Common mistakes
- comparing
arr[i]against everyj, includingj >= i, and double-counting - sorting the array in place when told not to modify it
- counting duplicates because the seen-before check is wrong
Optional extension challenge
Return the distinct values themselves into an output array (preserving first-seen order) as well as the count.
You can re-fetch the starter code for this question: prac_q84.c.
Question 85Spiral Fill 2D Array
Estimated time: 30-45 minutes
A generative art program wants to lay out sequential frame numbers in a spiral pattern across a square grid for a visual effect. Write a function that fills an n x n 2D array with the integers 1 to n * n in spiral order, starting at the top-left corner and spiralling clockwise inward.
The spiral proceeds rightwards along the top row, then downwards along the right column, then leftwards along the bottom row, then upwards along the left column, and continues inward in the same pattern.
Your task is to complete the function void spiral_fill(int grid[10][10], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q85.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q85.c:
gcc -Wall -Wextra -o prac_q85 prac_q85.c
./prac_q85
3
1 2 3
8 9 4
7 6 5
./prac_q85
1
1
./prac_q85
2
1 2
4 3Assumptions / Restrictions / Clarifications
1 <= n && n <= 10, matching the fixed array size.- Only the first
nxnregion ofgridneeds to be written. - The spiral always starts at
grid[0][0]with the value 1 and proceeds clockwise.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- 2D array indexing
- managing four moving boundaries
- layer-by-layer traversal
Worked example
For example:
For n = 3:
- the top row is filled left to right: 1 2 3.
- the right column (excluding the corner already filled) is filled top to bottom: 4 5.
- the bottom row is filled right to left: 6 7.
- the left column is filled bottom to top: 8.
- only the centre cell (9) remains and is filled last.
The resulting grid is [[1,2,3],[8,9,4],[7,6,5]].
Edge cases to consider
- a 1x1
grid - an even vs odd dimension where the centre is handled last
- a larger
gridfilling alln*nvalues
Common mistakes
- overwriting the centre cell twice, or missing it entirely
- off-by-one on the shrinking top/bottom/left/right bounds
- moving in the wrong order (should be right, down, left, up)
Optional extension challenge
Fill the spiral in the opposite (counter-clockwise) direction, or start the spiral from the centre outwards.
You can re-fetch the starter code for this question: prac_q85.c.
Question 86Find All Pairs Summing to Target
Estimated time: 20-30 minutes
A puzzle game challenges players to find how many distinct pairs of cards, drawn from a hand of uniquely numbered cards, sum to a target value shown on the screen. Write a function that, given an array of n distinct integers and a target sum, counts the number of unordered pairs of distinct elements in the array whose values add up to the target.
Each unordered pair should be counted exactly once, so the pair (1, 5) and the pair (5, 1) are the same pair and must not both be counted.
Your task is to complete the function int count_pairs_with_sum(int arr[], int n, int target), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q86.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q86.c:
gcc -Wall -Wextra -o prac_q86 prac_q86.c
./prac_q86
5
1 2 3 4 5
6
2
./prac_q86
5
1 2 3 4 5
100
0
./prac_q86
1
3
6
0Assumptions / Restrictions / Clarifications
- All values in
arrare distinct (no duplicate values). - A pair
(arr[i], arr[j])withi != jcounts once, regardless of order. - An element cannot be paired with itself, even if
targetis exactly double that element's value. nmay be 0 or 1, in which case there can be no pairs, and the function should return 0.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- nested loops over index pairs
- avoiding double counting
- counting rather than printing
Worked example
For example:
For arr = [1, 2, 3, 4, 5] and target = 6:
- the pair (1, 5) sums to 6 -- counts.
- the pair (2, 4) sums to 6 -- counts.
- every other unordered pair (1,2),(1,3),(1,4),(2,3),(2,5),(3,4),(3,5),(4,5) does not sum to 6.
count_pairs_with_sum returns 2.
Edge cases to consider
- an empty or single-element array (no pairs)
- a value that pairs with itself (i and j must differ)
- a
targetthat cannot be formed by any pair - multiple distinct pairs achieving the same sum
Common mistakes
- counting the pair (i, j) and (j, i) as two distinct pairs
- pairing an element with itself (i == j)
- off-by-one starting the inner loop at i instead of i+1
Optional extension challenge
Return the pairs themselves into an output 2D array instead of just returning the count.
You can re-fetch the starter code for this question: prac_q86.c.
Question 87Josephus Survivor (Array Simulation)
Estimated time: 30-45 minutes
A party game inspired by the classic Josephus problem eliminates players in a circle one by one until a single winner remains, and the organiser wants to be able to predict who will win before playing. n people numbered 0 to n - 1 stand in a circle. Starting at person 0 and counting around the circle, every kth remaining person is eliminated, until only one person remains.
Write a function that simulates this process using an array (marking eliminated people, or shrinking the array) and returns the number of the last remaining person.
Your task is to complete the function int josephus_survivor(int n, int k), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q87.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q87.c:
gcc -Wall -Wextra -o prac_q87 prac_q87.c
./prac_q87
7 3
3
./prac_q87
1 5
0
./prac_q87
4 1
3Assumptions / Restrictions / Clarifications
n >= 1,k >= 1.- If
n == 1, the sole person (0) survives immediately, with no eliminations. - Counting wraps around the circle, skipping over people who have already been eliminated.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- circular array simulation
- marking elements as eliminated
- modular index stepping
Worked example
For example:
For n = 4, k = 1 (every 1st remaining person is eliminated):
- starting at person 0: person 0 is eliminated first, then 1, then 2, in order.
- only person 3 is left.
josephus_survivor returns 3.
Edge cases to consider
- a single person (they survive)
- a step count
kof 1 (eliminate in order) klarger than the number of people (wrap around)- two people
Common mistakes
- counting eliminated people when stepping
kpositions - off-by-one so the wrong person is eliminated each round
- an infinite loop when the last survivor is not detected
Optional extension challenge
Return the full elimination order, not just the final survivor, and compare against the closed-form recurrence.
You can re-fetch the starter code for this question: prac_q87.c.
Question 88Validate Sudoku Row/Column
Estimated time: 30-40 minutes
A Sudoku helper app wants to give players live feedback about whether the row or column they just edited still obeys the game's rules. Write a function that checks whether a given row (or column) of a 9x9 Sudoku grid, passed as an array of 9 integers each between 1 and 9 inclusive with 0 representing an empty cell, contains any duplicate non-zero values.
Return 1 if the row is valid (no duplicate non-zero values), 0 otherwise. The same function can be used to validate either a row or a column, since both are just passed in as an array of 9 integers.
Your task is to complete the function int is_valid_line(int line[9]), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q88.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q88.c:
gcc -Wall -Wextra -o prac_q88 prac_q88.c
./prac_q88
5 3 0 0 7 0 0 0 0
1
./prac_q88
5 3 0 0 7 0 0 0 5
0
./prac_q88
5 3 4 6 7 8 9 1 2
1Assumptions / Restrictions / Clarifications
- Each element of
lineis between 0 and 9 inclusive. - 0 means empty and never counts as a duplicate, so a
linemay contain any number of 0s without being invalid. linealways has exactly 9 elements, matching a Sudoku row or column.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- scanning a
linefor duplicates - a
seen[]presence table - distinguishing filled from empty cells
Worked example
For example:
For line = [5, 3, 0, 0, 7, 0, 0, 0, 5]:
- 5 is seen for the first time at index 0 -- mark it seen.
- 3 and 7 are each seen once, no conflicts.
- the 0s are ignored throughout.
- at index 8, 5 is seen again, which is a duplicate non-zero value.
is_valid_line returns 0.
Edge cases to consider
- a completely empty
line(no filled cells) which is valid - a
linewith a single duplicate pair - a fully filled valid
line1..9 - handling the 0 (blank) marker correctly
Common mistakes
- treating repeated blank/0 cells as duplicates
- indexing the
seen[]table with the digit without adjusting for 1-based values - checking only rows or only columns when both are required
Optional extension challenge
Extend the check to the nine 3x3 boxes as well, validating a whole board rather than a single line.
You can re-fetch the starter code for this question: prac_q88.c.
Question 89Word Frequency Counter
Estimated time: 30-40 minutes
A simple text-mining tool has already split a document into an array of individual lowercase words and wants to know how often a particular word of interest appears. Given an array of n lowercase words (each a null-terminated string, no spaces) and a target word, write a function that counts how many times the target word appears in the array (exact match).
Matching must be an exact, whole-word comparison; partial matches (e.g. "cats" matching a search for "cat") should not be counted.
Your task is to complete the function int word_frequency(char *words[], int n, char *target), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q89.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q89.c:
gcc -Wall -Wextra -o prac_q89 prac_q89.c
./prac_q89
5
cat dog cat bird cat
cat
3
./prac_q89
5
cat dog cat bird cat
fish
0
./prac_q89
0
cat
0Assumptions / Restrictions / Clarifications
nmay be 0, in which case the function should return 0.- Comparison is exact and case-sensitive.
targetis always a valid, non-NULL, null-terminated string.- Words in the array may repeat any number of times.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- arrays of strings
- comparing
wordswithstrcmp - counting occurrences of a query
Worked example
For example:
For words = ["cat", "dog", "cat", "bird", "cat"] and target = "cat":
words[0]"cat" matches exactly -- counts.words[1]"dog" does not match.words[2]"cat" matches -- counts.words[3]"bird" does not match.words[4]"cat" matches -- counts.
word_frequency returns 3.
Edge cases to consider
- an empty document (no
words) - a query word that does not occur
- case differences between the query and the
words - the query occurring as a substring but not a whole word
Common mistakes
- using
==to compare strings instead ofstrcmp - matching substrings rather than whole
words - off-by-one over the array of
words
Optional extension challenge
Return the most frequent word in the document rather than counting a single supplied query word.
You can re-fetch the starter code for this question: prac_q89.c.
Question 90Linked List: Find Middle Node
Estimated time: 25-35 minutes
Note prac_q90.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A media player wants to jump straight to roughly the middle of a playlist stored as a singly linked list, without first walking the whole list to count how many songs it contains. A singly linked list of integers is defined by the provided node struct.
Write a function that returns a pointer to the middle node of the list. If the list has an even number of nodes, return the second of the two middle nodes. You should do this in a single traversal using two pointers moving at different speeds (the classic 'slow/fast pointer' technique), not by first counting the length.
Your task is to complete the function struct node *find_middle(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q90.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q90.c:
gcc -Wall -Wextra -o prac_q90 prac_q90.c
./prac_q90
5
1 2 3 4 5
3
./prac_q90
4
1 2 3 4
3
./prac_q90
1
9
9Assumptions / Restrictions / Clarifications
- The list is never empty when this function is called.
- You must use the slow/fast pointer technique in a single pass; counting the length first and then traversing again is not an acceptable solution.
- For an even-length list, the second of the two middle nodes is returned.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- slow/fast pointer technique
- single-pass traversal
- even vs odd length handling
Worked example
For example:
For the list 1 -> 2 -> 3 -> 4 -> NULL (even length):
slowandfastboth start at node 1.- step 1:
slowmoves to 2,fastmoves to 3. - step 2:
slowmoves to 3,fastmoves toNULL(sincefast->next->nextruns off the end), so the loop stops.
find_middle returns the node containing 3, the second of the two middle nodes (2 and 3).
Edge cases to consider
- a single-node list (that node is the middle)
- an even-length list (the second middle node is returned)
- a two-node list
- an odd-length list
Common mistakes
- advancing the fast pointer without a
NULLcheck and dereferencingNULL - returning the wrong middle for even-length lists
- using
length / 2with an off-by-one after counting nodes
Optional extension challenge
Return both middle nodes for an even-length list, or the exact middle for odd.
You can re-fetch the starter code for this question: prac_q90.c.
Question 91Validate Date
Estimated time: 25-35 minutes
A booking form must reject impossible dates before they reach the database. This is a defensive-programming exercise: write a function that returns 1 if the given day, month and year form a valid calendar date and 0 otherwise, rejecting every malformed combination.
A valid date has a month from 1 to 12, a positive year, and a day from 1 to the number of days in that month, accounting for leap years in February.
Your task is to complete the function int is_valid_date(int day, int month, int year), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q91.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q91.c:
gcc -Wall -Wextra -o prac_q91 prac_q91.c
./prac_q91
29 2 2024
1
./prac_q91
29 2 2023
0
./prac_q91
31 4 2021
0
./prac_q91
15 13 2020
0
./prac_q91
1 1 1
1Assumptions / Restrictions / Clarifications
- Year must be at least 1.
- Month must be in the range 1 to 12.
- Day must be at least 1 and at most the number of days in the given month.
- February has 29 days in a leap year and 28 otherwise; use the standard leap-year rule.
- Do not print anything inside
is_valid_date.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- input validation / defensive programming
- lookup tables for month lengths
- combining several guard conditions
Worked example
For example:
For 29/2/2023:
- the month 2 is in range and the year is positive.
- 2023 is not a leap year, so February has only 28 days.
- day 29 exceeds 28, so
is_valid_datereturns 0.
Edge cases to consider
- 29 February in leap and non-leap years
- the 31st day in a 30-day month
- a month number of 0 or 13 (out of range)
- day zero or a negative year
Common mistakes
- forgetting the leap-year adjustment for February
- indexing the days array with
monthinstead ofmonth - 1 - validating only some fields and letting others through
Optional extension challenge
Return its ordinal position within the year (1-366) for a valid date, or -1 for an invalid one, in the same function family.
You can re-fetch the starter code for this question: prac_q91.c.
Question 92Insert Into Sorted Linked List
Estimated time: 25-35 minutes
Note prac_q92.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A priority mailbox keeps its messages in ascending order of a numeric key. Write a function that inserts a new value into an already-sorted singly linked list so that the list remains sorted in ascending order, returning a pointer to the (possibly new) head of the list.
Your task is to complete the function struct node *insert_sorted(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q92.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q92.c:
gcc -Wall -Wextra -o prac_q92 prac_q92.c
./prac_q92
4
1 3 5 7
4
1 3 4 5 7
./prac_q92
3
1 2 3
0
0 1 2 3
./prac_q92
2
1 2
9
1 2 9Assumptions / Restrictions / Clarifications
- The input list is already sorted in ascending order.
- You must allocate exactly one new node with
mallocfor the insertedvalue. - If the new
valueis smaller than every existingvalue, it becomes the newhead. - Duplicate values are allowed; place the new node before or after equal values (either is accepted).
- Return the
headof the resulting list.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- linked list insertion
mallocfor a single node- splicing pointers without losing the list
Worked example
For example:
For inserting 4 into the sorted list 1 3 5 7:
- 4 is not smaller than the
head(1), so we walk forward. - we stop when the next
value(5) is not less than 4, i.e. after the node 3. - the new node is spliced between 3 and 5, giving 1 3 4 5 7.
Edge cases to consider
- inserting into an empty list (new node becomes
head) - a
valuesmaller than thehead - a
valuelarger than every element (append at the tail) - duplicate values
Common mistakes
- forgetting to return the new
headwhen inserting at the front - linking
new_node->nextafter already overwritingcurr->next - not allocating a node, or leaking it on an early return
Optional extension challenge
Add a delete_sorted function that removes the first node with a given value while keeping the list sorted.
You can re-fetch the starter code for this question: prac_q92.c.
Question 93Delete First Node With Value
Estimated time: 25-35 minutes
Note prac_q93.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A task manager stores its jobs in a singly linked list and needs to cancel a job by value. Write a function that deletes the FIRST node whose value equals the given target, frees that node, and returns a pointer to the (possibly new) head of the list.
If no node has the target value, the list is returned unchanged.
Your task is to complete the function struct node *delete_value(struct node *head, int value), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q93.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q93.c:
gcc -Wall -Wextra -o prac_q93 prac_q93.c
./prac_q93
5
1 2 3 4 3
3
1 2 4 3
./prac_q93
3
1 2 3
1
2 3
./prac_q93
3
1 2 3
9
1 2 3Assumptions / Restrictions / Clarifications
- Only the first matching node should be removed, even if several nodes share the
value. - The removed node must be freed with
freeto avoid a memory leak. - If the target is not present, return the list unchanged.
- If the
headnode is the one removed, return the newhead.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- linked list deletion
- freeing an unlinked node
- handling
head-vs-interior removal
Worked example
For example:
For deleting value 3 from the list 1 2 3 4 3:
- the
head(1) does not match, so we walk forward looking at each next node. - the first node whose
valueis 3 is found; it is unlinked and freed. - only that first 3 is removed, leaving 1 2 4 3.
Edge cases to consider
- deleting the
headnode - the
valuenot being present (list unchanged) - an empty list
- several nodes sharing the
value(only the first goes)
Common mistakes
- freeing the node before saving its next pointer
- forgetting to
freethe removed node (a leak) - removing every matching node instead of only the first
Optional extension challenge
Write delete_all_values so it removes every node with the target value in a single traversal.
You can re-fetch the starter code for this question: prac_q93.c.
Question 94Dynamic 2D Array Builder
Estimated time: 30-45 minutes
A spreadsheet-like application needs to support grids of arbitrary size chosen at runtime, which rules out using a fixed-size 2D array. Write a function that dynamically allocates a rows x cols 2D array of integers on the heap (an array of rows pointers, each pointing to a dynamically allocated array of cols ints), initialises every element to 0, and returns a pointer to the array of row pointers.
Also write the matching free function that releases all memory allocated by the builder, given the same rows value used to build it, so that no dynamically allocated grid ever leaks memory.
Your task is to complete the functions int **make_grid(int rows, int cols) and void free_grid(int **grid, int rows), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q94.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q94.c:
gcc -Wall -Wextra -o prac_q94 prac_q94.c
./prac_q94
3 4
0 0 0 0
0 0 0 0
0 0 0 0
./prac_q94
3 4
1 2 9
0 0 0 0
0 0 9 0
0 0 0 0
./prac_q94
1 1
0Assumptions / Restrictions / Clarifications
rowsandcolsare each greater than 0.free_gridmust not leak any memory and must not doublefree.- You must use
malloc/calloc; do not use a fixed-size 2D array. - Every element of the newly created
gridmust be initialised to 0 beforemake_gridreturns. free_gridis always called with the samerowsvalue that was originally passed tomake_grid.
Stuck? Here's a hint
What this practises
This question gives you practice with:
mallocfor an array of row pointers- allocating each row
- freeing in the reverse order and avoiding leaks
Worked example
For example:
For rows = 3, cols = 4, with cell (1, 2) set to 9:
make_gridallocates 3 row pointers, each pointing to acalloc'd array of 4 ints (all zero).grid[1][2]is then set to 9 by main.- every other cell remains 0, since
calloczero-initialises memory.
The printed grid is three rows: "0 0 0 0", "0 0 9 0", "0 0 0 0".
Edge cases to consider
- a 1x1 single-cell
grid - a non-square (
rows != cols)grid - a single-row or single-column allocation
- a
gridwith larger dimensions
Common mistakes
- freeing the outer pointer before freeing each row (leaking the
rows) - not checking
mallocforNULLbefore writing - confusing
rowsand columns when indexinggrid[r][c]
Optional extension challenge
Allocate a 3D grid int ***make_3d(int d, int r, int c) and free it cleanly.
You can re-fetch the starter code for this question: prac_q94.c.
Question 95Linked List: Reverse In Place
Estimated time: 45-60 minutes
Note prac_q95.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A music player's recently-played history is stored as a singly linked list with the most recently added song at the head; the developers now want to be able to flip it around to show the oldest entries first, without using any extra memory for a second list. A singly linked list of integers is defined by the provided node struct. Write a function that reverses the list in place (rearranging the existing nodes' next pointers, without allocating any new nodes) and returns a pointer to the new head of the reversed list.
Because the reversal happens in place, after the call the original head node becomes the new tail of the list, and its next pointer must be updated accordingly.
Your task is to complete the function struct node *reverse_list(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q95.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q95.c:
gcc -Wall -Wextra -o prac_q95 prac_q95.c
./prac_q95
3
1 2 3
3 2 1
./prac_q95
1
5
5
./prac_q95
0Assumptions / Restrictions / Clarifications
- The list may be empty (
headisNULL), in which case returnNULL. - You must not allocate any new nodes; only rewire existing next pointers.
- You may not use any array or other auxiliary linked list to store nodes.
- A list with a single node should be returned unchanged (still pointing to itself, next
NULL).
Stuck? Here's a hint
What this practises
This question gives you practice with:
- pointer rewiring
- the prev/curr/next three-pointer pattern
- returning the new
head
Worked example
For example:
For the list 1 -> 2 -> 3 -> NULL:
prev = NULL,curr = 1. Savenext(2), point 1'snextatprev(NULL), advance:prev = 1,curr = 2.- save
next(3), point 2'snextatprev(1), advance:prev = 2,curr = 3. - save
next(NULL), point 3'snextatprev(2), advance:prev = 3,curr = NULL; the loop ends.
reverse_list returns prev, which is node 3, now heading the list 3 -> 2 -> 1 -> NULL.
Edge cases to consider
- an empty list (returns
NULL) - a single-node list (unchanged)
- a two-node list (the minimal real reversal)
- ensuring the old
head's next becomesNULL
Common mistakes
- losing the rest of the list by overwriting next before saving it
- returning the old
headinstead of the new one - creating a cycle by not terminating the tail with
NULL
Optional extension challenge
Reverse only the sub-list between the m-th and n-th nodes, leaving the rest of the list intact.
You can re-fetch the starter code for this question: prac_q95.c.
Question 96Debug and Fix: Linked List Length
Estimated time: 30-45 minutes
Note prac_q96.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
The starter file contains a function that is meant to return the number of nodes in a singly linked list, but it is broken in two ways: it crashes on an empty list, and even on a non-empty list it returns a count that is off by one. Your task is to find and fix both bugs.
This is a debugging exercise. Trace through the given loop carefully for a one-node list and for an empty list to see exactly where it goes wrong, then repair it so it counts every node and safely handles NULL.
Your task is to complete the function int list_length(struct node *head), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q96.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q96.c:
gcc -Wall -Wextra -o prac_q96 prac_q96.c
./prac_q96
3
10 20 30
3
./prac_q96
1
5
1
./prac_q96
0
0Assumptions / Restrictions / Clarifications
- After your fix, the length of an empty (
NULL) list must be 0. - The corrected function must count every node exactly once.
- Do not change the prototype or the main function.
- Do not
freeor modify the list insidelist_length.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- debugging linked-list traversal
- the difference between
currandcurr->nextas a loop guard NULLsafety
Worked example
For example:
For the list 10 20 30 (after fixing):
currwalks 10 -> 20 -> 30 ->NULL, incrementing count on each real node.- the loop stops when
currisNULL, having counted 3 nodes.
The unfixed starter loops while curr->next is non-NULL, so it stops one node early (returning 2) and dereferences NULL on an empty list.
Edge cases to consider
- an empty list, which must return 0 not crash
- a single-node list (must return 1)
- a long list
- confirming the last node is counted
Common mistakes
- looping on
curr->nextinstead ofcurr - dereferencing
headwithout aNULLcheck - fixing the crash but leaving the off-by-one (or vice versa)
Optional extension challenge
Add a recursive version and compare how each behaves on a very long list with respect to stack usage.
You can re-fetch the starter code for this question: prac_q96.c.
Question 97File-Based Student Records Loader
Estimated time: 50-70 minutes
Note prac_q97.c uses the following data type:
struct student {
char name[50];
int mark;
};An administrative tool needs to import student results from a comma-separated text file exported by another system, without knowing in advance how many students the file contains. Records of students are stored one per line in a text file, each line formatted as name,mark (e.g. Grace,95), with no spaces around the comma.
Write a function that opens a file with the given filename, reads all the records from it into a dynamically allocated array of struct student (defined in the provided starter header), and returns a pointer to that array via an output parameter, also writing the number of records read via a second output parameter.
Your task is to complete the function void load_students(char *filename, struct student **out_arr, int *out_count), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q97.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q97.c:
gcc -Wall -Wextra -o prac_q97 prac_q97.c
./prac_q97
students.csv
4 students loaded
arr[1]: Grace 95
./prac_q97
missing.csv
0 students loaded
./prac_q97
solo.csv
1 students loaded
arr[0]: Katherine 91Assumptions / Restrictions / Clarifications
- If the file cannot be opened, set
*out_arrtoNULLand*out_countto 0. - Each name is at most 49 characters (fits in the student struct's name field).
- The caller is responsible for freeing the returned array.
- The file may contain any number of records, including zero (an empty file).
- Lines are always well-formed as
name,markwith no surrounding whitespace.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- opening a file with
fopen - parsing lines with
fgets/fscanf - checking for read failure and closing the file
Worked example
For example:
For students.csv containing "Ada,88\nGrace,95\nAlan,95\nKatherine,91\n":
fopensucceeds, so the file is read line by line.- each line is parsed as
name,markwithfscanf, appending a newstruct studentto the dynamically-growing array. - 4 records are read in total: Ada 88, Grace 95, Alan 95, Katherine 91.
main prints "4 students loaded" and then "arr[1]: Grace 95", since arr[1] is the second record read.
Edge cases to consider
- a file that does not exist (
fopenreturnsNULL) - an empty file (zero records)
- a trailing newline or blank final line
- more records than the array can hold
Common mistakes
- not checking the return value of
fopenbefore reading - forgetting to
fclose, leaking the file handle - assuming a fixed number of lines instead of looping until EOF
Optional extension challenge
Compute and print class statistics (min, max, mean mark) as you load, and skip malformed lines gracefully.
You can re-fetch the starter code for this question: prac_q97.c.
Question 98Binary Search Tree Insert and Height
Estimated time: 35-50 minutes
Note prac_q98.c uses the following data type:
struct tree_node {
int value;
struct tree_node *left;
struct tree_node *right;
};A dictionary application wants to store a growing set of unique integer keys such that they stay easy to search and can also report how unbalanced the resulting structure has become. A binary search tree (BST) of integers is defined by the provided struct tree_node. Write two functions: one that inserts a new value into the BST (allocating a new node as needed) and returns the (possibly new) root of the tree, and one that computes the height of a tree.
The height of a tree is the number of nodes on the longest path from root to a leaf; an empty tree has height 0, and a tree with a single node has height 1.
Your task is to complete the functions struct tree_node *bst_insert(struct tree_node *root, int value) and int tree_height(struct tree_node *root), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q98.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q98.c:
gcc -Wall -Wextra -o prac_q98 prac_q98.c
./prac_q98
4
5 3 8 1
3
./prac_q98
0
0
./prac_q98
2
5 5
1Assumptions / Restrictions / Clarifications
- Duplicate values should not be inserted (leave the tree unchanged if
valuealready exists). bst_insertmust maintain the BST property: left subtree values < nodevalue< right subtree values.- The tree may be empty (
rootisNULL) at the start. bst_insertmust always have its result assigned back to therootvariable, since therootmay change when the tree was previously empty.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- recursive BST insertion
- maintaining the ordering invariant
- computing height recursively
Worked example
For example:
For inserting 5, 3, 8, 1 in order:
- 5 becomes the
root(empty tree). - 3 < 5, goes to the left of 5.
- 8 > 5, goes to the right of 5.
- 1 < 5, then 1 < 3, goes to the left of 3.
The tree is: 5 with left child 3 (which has left child 1) and right child 8.
The longest root-to-leaf path is 5 -> 3 -> 1, which has 3 nodes, so tree_height returns 3.
Edge cases to consider
- inserting into an empty tree
- inserting a duplicate
value - a degenerate tree from already-sorted inserts
- a single-node tree (height 1)
Common mistakes
- leaking nodes or losing the tree by not returning the (possibly new)
root - off-by-one in the height base cases
- placing values on the wrong side of a node
Optional extension challenge
Add a function int bst_is_balanced(struct tree_node *root) that checks whether the tree is AVL-balanced.
You can re-fetch the starter code for this question: prac_q98.c.
Question 99Custom Dynamic String Buffer (ADT)
Estimated time: 50-70 minutes
Note prac_q99.c uses the following data type:
struct strbuf {
char *data;
int length;
int capacity;
};A logging library needs to build up long log messages piece by piece (timestamps, tags, messages) without knowing the final length in advance, so a fixed-size char array is not suitable. Implement a simple growable string buffer ADT, struct strbuf (see the provided starter header), which stores a heap-allocated, null-terminated character array plus its current length and allocated capacity.
Write a function that creates a new, empty buffer, a function that appends a null-terminated C string onto the end of the buffer (growing the underlying allocation with realloc as needed, doubling capacity when full), and a function that frees all memory owned by the buffer.
Your task is to complete the functions struct strbuf strbuf_new(void), void strbuf_append(struct strbuf *buf, char *s) and void strbuf_free(struct strbuf *buf), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q99.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q99.c:
gcc -Wall -Wextra -o prac_q99 prac_q99.c
./prac_q99
2
Hello,
world!
Hello, world!
13
./prac_q99
1
0
./prac_q99
1
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
96Assumptions / Restrictions / Clarifications
- A newly created buffer represents the empty string (length 0).
strbuf_appendmay be called any number of times and must always keep the buffer's data null-terminated.strbuf_appendmust correctly grow the underlying allocation (viarealloc, doubling capacity) whenever there is not enough room for the appended text.strbuf_freemust not leak memory, and after being called,buf->datashould be set toNULL.
Stuck? Here's a hint
What this practises
This question gives you practice with:
realloc-based growable buffers- amortised doubling of capacity
- keeping length and capacity in sync
Worked example
For example:
For appending "Hello," then " world!":
strbuf_newcreates an empty buffer (length 0, capacity 0).- appending "Hello," (6 characters) needs 7 bytes including the terminator, more than capacity 0, so the buffer grows to capacity 8; data becomes "Hello,", length becomes 6.
- appending " world!" (7 characters) needs 6+7+1=14 bytes, more than capacity 8, so the buffer grows (doubling to 16); data becomes "Hello, world!", length becomes 13.
main prints "Hello, world!" then "13".
Edge cases to consider
- appending to a freshly created empty buffer
- appending a string longer than the current spare capacity
- appending the empty string
- many small appends forcing repeated growth
Common mistakes
- forgetting to null-terminate after each append
- using the old pointer after
reallocmoves the block - confusing length (used) with capacity (allocated) and overflowing
Optional extension challenge
Add insert-at-index and delete-range operations that shift the buffer contents correctly.
You can re-fetch the starter code for this question: prac_q99.c.
Question 100Undo Stack for a Text Editor
Estimated time: 50-70 minutes
Note prac_q100.c uses the following data type:
struct edit_node {
char text[200];
struct edit_node *next;
};A minimal editor wants to support an unlimited number of undo steps for a single line as the user edits it. This undo system stores previous versions of the line on a stack implemented as a singly linked list, using the provided edit_node struct, where the head of the list is the most recent version.
Write a function that pushes a new version of the line onto the stack (allocating a new node that copies the given string), and a function that pops and discards the most recent version, returning the contents of the version that becomes current (i.e. the new head) via a buffer supplied by the caller, or writing the empty string if the stack becomes empty.
Your task is to complete the functions struct edit_node *undo_push(struct edit_node *head, char *text) and struct edit_node *undo_pop(struct edit_node *head, char *current_out), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q100.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q100.c:
gcc -Wall -Wextra -o prac_q100 prac_q100.c
./prac_q100
2
hello
hello world
cur is now: hello
./prac_q100
1
only version
cur is now:
./prac_q100
3
v1
v2
v3
cur is now: v2Assumptions / Restrictions / Clarifications
- Strings passed to
undo_pushare at most 199 characters long. undo_popmustfreethe popped node.current_outpoints to a buffer at least 200 characters long.- If
undo_popempties the stack completely,current_outshould be set to the empty string and the function should returnNULL.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- stack ADT via a linked list
- push/pop discipline
- freeing popped nodes
Worked example
For example:
For pushing "v1", then "v2", then "v3":
- after all pushes, the stack (
headto tail) is v3 -> v2 -> v1 ->NULL. undo_popfrees theheadnode (v3) and returns the newhead, which is v2.current_outis set to the contents of the newhead, "v2".
main prints "cur is now: v2".
Edge cases to consider
- popping from an empty stack
- a single push then pop
- interleaved pushes and pops
- clearing the whole stack and freeing every node
Common mistakes
- memory-leaking popped nodes by not freeing them
- returning a value from an empty stack without signalling underflow
- losing the rest of the stack when unlinking the top node
Optional extension challenge
Add a redo stack so that popped (undone) actions can be re-applied until a new action clears the redo history.
You can re-fetch the starter code for this question: prac_q100.c.
Question 101Merge Two Sorted Linked Lists
Estimated time: 45-60 minutes
Note prac_q101.c uses the following data types:
struct node, a singly linked list node with an int value and a struct node *next (with helper functions build_list, print_list and free_list already provided for you in node.h)
A merge step in an external sort needs to combine two already-sorted runs held as linked lists into a single sorted list. Write a function that merges two ascending singly linked lists into one ascending list, reusing the existing nodes (not allocating new ones), and returns the head of the merged list.
Your task is to complete the function struct node *merge_sorted(struct node *a, struct node *b), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q101.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q101.c:
gcc -Wall -Wextra -o prac_q101 prac_q101.c
./prac_q101
3
1 3 5
3
2 4 6
1 2 3 4 5 6
./prac_q101
0
3
1 2 3
1 2 3
./prac_q101
2
1 2
0
1 2Assumptions / Restrictions / Clarifications
- Both input lists are already sorted in ascending order.
- Reuse the existing nodes by relinking them; do not allocate new nodes.
- Either or both input lists may be empty (
NULL). - The merged list must contain every node from both inputs, in ascending order.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- merging two linked lists
- the dummy-head technique
- relinking nodes without allocation
Worked example
For example:
For list A = 1 3 5 and list B = 2 4 6:
- the heads 1 and 2 are compared; 1 is smaller and is linked first.
- then 3 vs 2 links 2, then 3 vs 4 links 3, and so on.
- when one list runs out, the remainder of the other is attached, giving 1 2 3 4 5 6.
Edge cases to consider
- one list empty and the other not
- both lists empty (result
NULL) - lists with equal values across both inputs
- all of one list smaller than the other
Common mistakes
- allocating new nodes and leaking or duplicating the originals
- forgetting to attach the leftover tail of the longer list
- losing the head by not using a dummy node or saving the first result
Optional extension challenge
Merge k sorted lists (given an array of heads) efficiently, for example by repeatedly merging pairs.
You can re-fetch the starter code for this question: prac_q101.c.
Question 102Count Words in a File
Estimated time: 40-55 minutes
A word-count utility (like the Unix wc -w) needs to count the words in a text file. Write a function that opens the file with the given name, counts the number of whitespace-separated words in it, and returns that count.
A word is any maximal run of non-whitespace characters. Words may be separated by spaces, tabs or newlines, and there may be leading or trailing whitespace.
Your task is to complete the function int count_words_in_file(char *filename), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q102.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q102.c:
gcc -Wall -Wextra -o prac_q102 prac_q102.c
./prac_q102
poem.txt
9
./prac_q102
empty.txt
0
./prac_q102
missing.txt
-1Assumptions / Restrictions / Clarifications
- If the file cannot be opened, return -1.
- Whitespace includes spaces, tabs and newlines.
- Runs of multiple whitespace characters separate a single pair of words (no empty words).
- Remember to close the file before returning.
- An empty file contains 0 words.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- file handling with
fopen/fgetc/fclose - checking for open failure
- word-boundary state tracking
Worked example
For example:
For poem.txt containing "the quick brown fox\njumps over the lazy dog\n":
- the function reads character by character, starting a new word on each transition from whitespace to non-whitespace.
- the words are: the, quick, brown, fox, jumps, over, the, lazy, dog.
- that is 9 words, so
count_words_in_filereturns 9.
Edge cases to consider
- a file that cannot be opened (return -1)
- an empty file (0 words)
- leading/trailing whitespace and blank lines
- words separated by tabs or multiple spaces
Common mistakes
- not checking
fopenforNULLbefore reading - forgetting to
fclosethe file (leaking the handle) - counting whitespace runs as extra words
Optional extension challenge
Also return the line count and character count via output parameters, reproducing all three wc figures.
You can re-fetch the starter code for this question: prac_q102.c.
Question 103Command-Line RPN Calculator
Estimated time: 45-60 minutes
A scripting tool evaluates small arithmetic expressions entered as a line of whitespace-separated tokens in Reverse Polish Notation (RPN, also called postfix). In RPN, operators come after their operands, so 3 4 + means 3 + 4 and 5 1 2 + 4 * + means 5 + ((1 + 2) * 4). Write a function that evaluates such an expression given as an array of string tokens and returns the result.
Each token is either an integer literal or one of the four operators +, -, * and /. Evaluate the expression using an explicit stack: push numbers, and on an operator pop the top two values, apply it, and push the result.
Your task is to complete the function long evaluate_rpn(int count, char *tokens[]), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q103.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q103.c:
gcc -Wall -Wextra -o prac_q103 prac_q103.c
./prac_q103
3 4 +
7
./prac_q103
5 1 2 + 4 * + 3 -
14
./prac_q103
2 3 4 * +
14Assumptions / Restrictions / Clarifications
countis the number oftokens;tokens[0..count-1]are the token strings.- The four supported operators are
+,-,*and/, all using integer arithmetic. - For a binary operator, the second-popped value is the left operand: for
tokensa b -the result isa - b. - You may assume the expression is well-formed and never divides by zero.
- Return the single value left on the stack.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- command-line argument handling
- an explicit array-based stack
- postfix (RPN) evaluation and operator dispatch
Worked example
For example:
For the tokens 5 1 2 + 4 * + 3 -:
- push 5, 1, 2;
+pops 1 and 2 and pushes 3. - push 4;
*pops 3 and 4 and pushes 12;+pops 5 and 12 and pushes 17. - push 3;
-pops 17 and 3 and pushes 14. - the final value on the stack is 14.
Edge cases to consider
- a single number with no operators
- operand order for non-commutative
-and/ - negative results
- distinguishing the operator
-from a negative-number token (single-charcheck)
Common mistakes
- popping the operands in the wrong order for
-and/ - treating a multi-character token like
-5as the minus operator - reading the result from the wrong stack position
Optional extension challenge
Support unary minus and a modulo operator, and detect malformed expressions (too few operands) instead of assuming well-formed input.
You can re-fetch the starter code for this question: prac_q103.c.
Question 104Generic Dynamic Stack (typedef ADT)
Estimated time: 45-60 minutes
Note prac_q104.c uses the following data type:
typedef struct {
int *data;
int size;
int capacity;
} IntStack;A calculator engine needs a reusable stack of integers that grows on demand so it never overflows a fixed capacity. Implement a small abstract data type: an IntStack backed by a dynamically allocated array that doubles its capacity whenever it fills up.
The IntStack type is a typedef struct provided in the starter header, holding a pointer to the data, the current size, and the current capacity. Implement the four operations below.
Your task is to complete the functions void stack_init(IntStack *s), void stack_push(IntStack *s, int value), int stack_pop(IntStack *s) and void stack_free(IntStack *s), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q104.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q104.c:
gcc -Wall -Wextra -o prac_q104 prac_q104.c
./prac_q104
1 2 3 p p p
3
2
1
./prac_q104
10 p 20 30 p
10
30
./prac_q104
5 5 p p
5
5Assumptions / Restrictions / Clarifications
stack_initsets up an empty stack with a small initial capacity allocated withmalloc.stack_pushappends avalue, doubling the capacity withreallocwhen the array is full.stack_popremoves and returns the most recently pushedvalue; you may assume it is never called on an empty stack.stack_freereleases the allocated array and leaves the stack empty; it must not leak memory.- Keep size and capacity consistent at all times.
Stuck? Here's a hint
What this practises
This question gives you practice with:
typedef structADTsmalloc/realloc/freediscipline- amortised doubling of capacity
- last-in-first-out semantics
Worked example
For example:
For the arguments 1 2 3 p p p:
- 1, 2 and 3 are pushed, growing the array as needed.
- the three
ptokens pop and print 3, then 2, then 1 (last in, first out).
so the program prints 3, 2 and 1 on separate lines.
Edge cases to consider
- pushing enough values to force at least one
realloc - popping down to an empty stack
- interleaved pushes and pops
- freeing without leaking the backing array
Common mistakes
- using the old data pointer after
reallocmoves the block - confusing size (used) with capacity (allocated) when deciding to grow
- forgetting to
freethe array instack_free, leaking memory
Optional extension challenge
Add stack_peek, which returns the top without popping, and stack_shrink, which halves capacity when the stack is mostly empty.
You can re-fetch the starter code for this question: prac_q104.c.
Question 105Recursive Directory Size Simulation
Estimated time: 50-70 minutes
Note prac_q105.c uses the following data type:
#define MAX_CHILDREN 20
struct fs_node {
char name[50];
int is_directory;
long size;
struct fs_node *children[MAX_CHILDREN];
int num_children;
};A disk-usage analyser tool (similar to the Unix du command) needs to report how much space a directory and everything nested inside it occupies. A simplified filesystem is modelled as a tree: each struct fs_node (see the provided starter header) is either a file (with a size in bytes and no children) or a directory (size field unused, with a list of child struct fs_node pointers via a children array and num_children count).
Write a recursive function that computes the total size of a node: for a file, its own size; for a directory, the sum of the total sizes of all its children (recursively, since children may themselves be directories).
Your task is to complete the function long total_size(struct fs_node *node), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q105.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q105.c:
gcc -Wall -Wextra -o prac_q105 prac_q105.c
./prac_q105
3
100 250 50
400
./prac_q105
1
42
42
./prac_q105
0
0Assumptions / Restrictions / Clarifications
- A
nodeis a file ifis_directory == 0, in which case use itssizefield. - A
nodeis a directory ifis_directory == 1, in which case sum overchildren[0..num_children-1]. - Directories may be empty (
num_children == 0), contributing 0. - The tree may be arbitrarily deep, but is guaranteed to have no cycles.
num_childrennever exceedsMAX_CHILDRENas defined in the starter header.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- recursion over a tree of nodes
- summing across children
- distinguishing files from directories
Worked example
For example:
For file sizes 100, 250, 50 (100 and 250 directly in root/, 50 nested in root/sub/):
rootis a directory, sototal_sizesums over its children: the two direct files (100, 250) and thesub/directory.total_sizeonsub/recurses: it is a directory containing one file of size 50, so it returns 50.- summing: 100 + 250 + 50 = 400.
total_size(&root) returns 400.
Edge cases to consider
- an empty directory (size 0)
- a single file
- deeply nested directories
- a directory containing only empty subdirectories
Common mistakes
- not recursing into subdirectories, so nested files are missed
- double-counting a directory's own size and its children's
- a missing base case causing unbounded recursion
Optional extension challenge
Also return the deepest nesting level and the single largest file found anywhere in the tree.
You can re-fetch the starter code for this question: prac_q105.c.
Question 106Polynomial Linked List Evaluation
Estimated time: 50-70 minutes
Note prac_q106.c uses the following data type:
struct term {
int coefficient;
int exponent;
struct term *next;
};A computer algebra tool needs to represent sparse polynomials (with many zero coefficients) efficiently, and support evaluating them at a point and adding two polynomials together. A polynomial is represented as a singly linked list of terms, where each term (see the provided starter header) stores an integer coefficient and a non-negative integer exponent, with terms in the list guaranteed to have strictly decreasing exponents.
Write a function that evaluates the polynomial at a given double value of x, and a function that adds two polynomials together, returning a newly allocated list representing the sum (also with strictly decreasing exponents, and with any zero-coefficient terms omitted), without modifying the two input lists.
Your task is to complete the functions double poly_evaluate(struct term *poly, double x) and struct term *poly_add(struct term *poly1, struct term *poly2), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q106.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q106.c:
gcc -Wall -Wextra -o prac_q106 prac_q106.c
./prac_q106
2.0 3 2 2 1 1 0 , -3 2 5 1
17
7x^1 + 1x^0
./prac_q106
5.0 ,
0
0 (the zero polynomial)
./prac_q106
0.0 2 1 , -2 1
0
0 (the zero polynomial)Assumptions / Restrictions / Clarifications
- Either polynomial may be the empty list (
NULL), representing the zero polynomial. poly_addmust allocate new nodes for the result and must not modify or share nodes withpoly1orpoly2.- If the resulting sum is the zero polynomial,
poly_addshould returnNULL. - Terms with the same exponent in
poly1andpoly2should be combined into a single term in the result (summing their coefficients). - The exponents in the returned list must remain strictly decreasing, as in the input lists.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- sparse representation with linked nodes
- evaluating a polynomial at a point
- power/coefficient handling
Worked example
For example:
For poly1 = 3x^2 + 2x^1 + 1x^0 evaluated at x = 2.0:
- the
3x^2term contributes3 * 2 * 2 = 12. - the
2x^1term contributes2 * 2 = 4. - the
1x^0term contributes1. 12 + 4 + 1 = 17, sopoly_evaluatereturns17.
For poly_add with poly2 = -3x^2 + 5x^1: the x^2 terms cancel (3 + -3 = 0, omitted), the x^1 terms combine (2 + 5 = 7), and poly1's x^0 term (1) has no match in poly2, so the sum is 7x^1 + 1x^0.
Edge cases to consider
- the zero polynomial (empty list)
- a single constant term (exponent 0)
- evaluating at
x= 0 andx= 1 - large exponents where naive powers may overflow
Common mistakes
- recomputing
x^n from scratch each term instead of reusing work - skipping the constant term when the exponent is 0
- integer overflow from large exponents or coefficients
Optional extension challenge
Add polynomial addition that merges two sorted-by-exponent lists, combining like terms.
You can re-fetch the starter code for this question: prac_q106.c.
Question 107Maze Solver (Recursive Backtracking)
Estimated time: 55-75 minutes
A simple puzzle game generates rectangular mazes and needs to check whether a maze is actually solvable before presenting it to the player. A maze is represented as a rows x cols 2D array of characters, where '#' denotes a wall, '.' denotes open floor, 'S' denotes the start cell and 'E' denotes the end cell.
Write a recursive function that determines whether there is a path from the start cell to the end cell, moving only up, down, left or right through open floor cells (not walls), without revisiting a cell already on the current path. To avoid revisiting cells, you should mark visited floor cells (e.g. by temporarily changing them to '#' and restoring them, or using a separate visited array).
Your task is to complete the function int has_path(char maze[20][20], int rows, int cols, int sr, int sc, int er, int ec), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q107.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q107.c:
gcc -Wall -Wextra -o prac_q107 prac_q107.c
./prac_q107
5 5
S.#..
.##..
...#.
##.#.
....E
0 0 4 4
1
./prac_q107
3 3
S#E
###
...
0 0 0 2
0
./prac_q107
5 5
S.#..
.##..
...#.
##.#.
....E
0 0 0 0
1Assumptions / Restrictions / Clarifications
rowsandcolsare each at most 20.(sr, sc)and(er, ec)are guaranteed to be valid in-bounds cells that are not walls.- The function must terminate for any
maze, including one with no valid path. - Movement is restricted to the four orthogonal directions (up, down, left, right); diagonal movement is not allowed.
- The start and end cells may be the same cell, in which case a path trivially exists.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- recursive depth-first search
- backtracking by unmarking on failure
- marking visited cells
Worked example
For example:
For the maze S.#..\n.##..\n...#.\n##.#.\n....E with start (0,0) and end (4,4):
- the recursive search tries moving down/right from the start, backtracking out of dead ends (marking visited cells as '#' temporarily so it never revisits them on the current path).
- a path exists, e.g.
(0,0)->(1,0)->(2,0)->(2,1)->(2,2)->(3,2)->(4,2)->(4,3)->(4,4).
has_path returns 1.
Edge cases to consider
- start cell equals the goal cell
- a
mazewith no possible path - a
mazethat is entirely open - a 1x1
maze
Common mistakes
- not marking cells visited, causing infinite recursion between two cells
- forgetting to try all four directions
- marking a cell but never unmarking it when backtracking (if the algorithm needs it)
Optional extension challenge
Return the length of the shortest path (not just whether a path exists) by exploring breadth-first instead.
You can re-fetch the starter code for this question: prac_q107.c.
Question 108Priority Task Queue (Linked List ADT)
Estimated time: 55-75 minutes
Note prac_q108.c uses the following data type:
struct task_node {
char name[50];
int priority;
struct task_node *next;
};An emergency dispatch system needs to always serve its most urgent outstanding task next, while still processing equally urgent tasks in the order they were reported. Implement a queue of tasks using a singly linked list kept sorted in descending order of priority (higher priority value = more urgent), using the provided task_node struct.
Write a function that inserts a new task into the correct sorted position (allocating a new node), and a function that removes and returns the most urgent task (the head of the list), returning it via an output parameter and freeing its node. Ties in priority should be broken by insertion order (earlier-inserted tasks of equal priority come first).
Your task is to complete the functions struct task_node *pq_insert(struct task_node *head, char *name, int priority) and struct task_node *pq_pop(struct task_node *head, char *name_out, int *priority_out), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q108.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q108.c:
gcc -Wall -Wextra -o prac_q108 prac_q108.c
./prac_q108
backup 1 fire 9 email 3
Popped: fire 9
./prac_q108
first 5 second 5
Popped: first 5
./prac_q108
only 2
Popped: only 2Assumptions / Restrictions / Clarifications
- The list may be empty (
headisNULL) when either function is called. pq_popis never called on an empty list.pq_popmustfreethe node it removes and return the newheadof the list.name_outpoints to a buffer at least 50 characters long, supplied by the caller.- When two tasks share the same priority, the one inserted earlier must be popped first.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- sorted linked-list insertion
- keeping the highest priority at the front
- dequeue and
free
Worked example
For example:
For inserting backup 1, fire 9, email 3, in that order:
- backup 1 is inserted into the empty list: [backup 1].
- fire 9 has higher priority than backup 1, so it goes to the front: [fire 9, backup 1].
- email 3 is less urgent than fire 9 but more urgent than backup 1, so it goes in between: [fire 9, email 3, backup 1].
pq_pop removes and returns the head, fire 9, so main prints "Popped: fire 9".
Edge cases to consider
- inserting into an empty queue
- a new task with the highest priority (new
head) - equal priorities (define FIFO vs LIFO tie-break)
- dequeueing until the queue is empty
Common mistakes
- inserting at the
headunconditionally and breaking the ordering - losing the list when splicing in a new node
- leaking the node that is dequeued
Optional extension challenge
Support changing the priority of an already-queued task, re-positioning it to keep the queue ordered.
You can re-fetch the starter code for this question: prac_q108.c.
Question 109Graph Reachability via Adjacency Matrix
Estimated time: 50-70 minutes
A flight-booking system models direct flight routes between airports as a directed graph, and must check whether one airport can reach another through some sequence of flights. A directed graph of n vertices (0 .. n - 1) is represented as an n x n adjacency matrix of ints, where matrix[i][j] is 1 when the directed edge i -> j exists, and 0 otherwise.
Write a recursive function using depth-first search that determines whether vertex to can be reached starting at vertex from by following directed edges (a vertex is always self-reachable). You will need a helper visited array that prevents infinite recursion on cyclic graphs.
Your task is to complete the function int is_reachable(int matrix[20][20], int n, int from, int to), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q109.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q109.c:
gcc -Wall -Wextra -o prac_q109 prac_q109.c
./prac_q109
5 0-1 1-2 2-0 3-4 0 2
1
./prac_q109
5 0-1 1-2 2-0 3-4 0 4
0
./prac_q109
5 0-1 1-2 2-0 3-4 3 3
1Assumptions / Restrictions / Clarifications
nis at most 20, matching the fixed array size.- The graph may contain cycles; your traversal must not loop forever.
0 <= from && from < nand0 <= to && to < n.- A vertex is always considered self-reachable, even with no outgoing edges (
from == toshould return 1).
Stuck? Here's a hint
What this practises
This question gives you practice with:
- adjacency-
matrixgraphs - depth-first or breadth-first traversal
- a
visited[]array
Worked example
For example:
For edges 0-1, 1-2, 2-0, 3-4 and from = 0, to = 2:
- the search starts at vertex 0, marking it visited.
- it follows edge
0->1; vertex 1 is then marked visited. - it follows edge
1->2and reaches the target vertexto.
is_reachable returns 1, since the path 0 -> 1 -> 2 reaches 2 when starting at 0.
Edge cases to consider
- source and destination are equal (reachable trivially)
- a disconnected graph where the destination is unreachable
- a graph with self-loops
- a cycle that must not cause infinite looping
Common mistakes
- revisiting nodes and looping forever without a
visited[]array - treating the
matrixas undirected when it is directed (or vice versa) - indexing the
matrixwith the vertices swapped
Optional extension challenge
Return the length of the shortest path between two vertices using breadth-first search over the matrix.
You can re-fetch the starter code for this question: prac_q109.c.
Question 110Doubly Linked List Deque ADT
Estimated time: 55-75 minutes
Note prac_q110.c uses the following data type:
struct dnode {
int value;
struct dnode *prev;
struct dnode *next;
};
struct deque {
struct dnode *head;
struct dnode *tail;
int size;
};A sliding-window algorithm library needs a double-ended queue that supports efficient insertion and removal at both ends, which a singly linked list or plain array cannot provide as cleanly. Implement a double-ended queue (deque) of integers using a doubly linked list, with dnode and deque structs provided in the starter header (the struct deque holds head and tail pointers plus a size count).
Write functions to push a value onto the front, push a value onto the back, pop a value from the front (removing and returning it via an output parameter, returning 1 on success or 0 if the deque was empty), and pop a value from the back (same contract). All four operations must run in O(1) time and must correctly maintain both prev and next pointers as well as the deque's head, tail and size fields in every case, including when the deque becomes empty or has exactly one element.
Your task is to complete the functions void deque_push_front(struct deque *d, int value), void deque_push_back(struct deque *d, int value), int deque_pop_front(struct deque *d, int *value_out) and int deque_pop_back(struct deque *d, int *value_out), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q110.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q110.c:
gcc -Wall -Wextra -o prac_q110 prac_q110.c
./prac_q110
PB 2 PB 3 PF 1 PRINT POPB POPF
Deque: 1 2 3
Pop back: 3
Pop front: 1
./prac_q110
POPF
Pop front: failed, deque is empty
./prac_q110
PB 7 POPF
Pop front: 7Assumptions / Restrictions / Clarifications
- A newly created deque has
head == NULL,tail == NULL,size == 0. - All push/pop functions must correctly update
size. - Popped nodes must be freed; no memory may be leaked.
- After popping the only remaining element, both
headandtailmust becomeNULL. - Popping from an empty deque must return 0 and leave
value_outunwritten (or unspecified), without crashing.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- doubly linked lists with
headandtail - insert/remove at both ends
- maintaining
prevandnextlinks
Worked example
For example:
For tokens PB 2, PB 3, PF 1, PRINT, POPB, POPF:
push_back(2): deque is [2].push_back(3): deque is [2, 3].push_front(1): deque is [1, 2, 3].PRINTprints "Deque: 1 2 3".pop_backremoves 3 from the tail, printing "Pop back: 3"; deque is now [1, 2].pop_frontremoves 1 from the head, printing "Pop front: 1"; deque is now [2].
Edge cases to consider
- operations on an empty deque
- a deque with a single element (
head == tail) - removing the last element (
headandtailmust becomeNULL) - alternating front/back operations
Common mistakes
- updating
nextbut forgetting the matchingprevpointer - not updating
tailwhen removing from the front (orheadwhen removing from the back) - leaking removed nodes or dereferencing
NULLon an empty deque
Optional extension challenge
Add a function that reverses the deque in O(1) by conceptually swapping the roles of prev/next and head/tail.
You can re-fetch the starter code for this question: prac_q110.c.
Question 111Hash Table with Chaining (Simplified)
Estimated time: 55-75 minutes
Note prac_q111.c uses the following data type:
#define TABLE_SIZE 101
struct hash_node {
char key[50];
int value;
struct hash_node *next;
};
// A simple djb2-style string hash, already implemented for you.
static unsigned int hash_string(char *key) {
unsigned int hash = 5381;
for (int i = 0; key[i] != '\0'; i++) {
hash = hash * 33 + (unsigned char) key[i];
}
return hash % TABLE_SIZE;
}A configuration system for an application needs fast lookup of string-named settings (e.g. "volume" -> 80, "brightness" -> 50) without scanning a list every time. Implement a very small string-to-int hash table using separate chaining, backed by a fixed-size array of TABLE_SIZE linked-list buckets, using the provided starter header (which defines TABLE_SIZE, a struct hash_node for chain entries, and provides a simple hash function hash_string, already implemented for you).
Write a function that inserts a key/value pair into the table (or updates the value if the key already exists, without creating a duplicate node), and a function that looks up a key, returning 1 and writing the value via an output parameter if found, or returning 0 if not found.
Your task is to complete the functions void hash_insert(struct hash_node *table[], char *key, int value) and int hash_lookup(struct hash_node *table[], char *key, int *value_out), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q111.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q111.c:
gcc -Wall -Wextra -o prac_q111 prac_q111.c
./prac_q111
apples 5 bananas 12 apples 7 LOOKUP apples pears
found, value 7
not found
./prac_q111
LOOKUP anything
not found
./prac_q111
count 1 count 2 count 3 LOOKUP count
found, value 3Assumptions / Restrictions / Clarifications
tableis an array ofTABLE_SIZEbucket head pointers, each initiallyNULL.- Keys are at most 49 characters long.
hash_insertmust allocate new nodes withmallocas needed and must not leak memory when updating an existingkey'svalue(it must not allocate a new node if thekeyalready exists).- You should use the provided
hash_stringfunction to select a bucket. - Two different keys may hash to the same bucket (a collision); such keys must both be stored correctly in that bucket's chain.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- hashing keys into buckets
- separate chaining with linked lists
- insert and lookup
Worked example
For example:
For inserting apples 5, bananas 12, apples 7 (in that order), then looking up apples:
- apples 5 is inserted as a new node.
- bananas 12 is inserted as a new node (in whichever bucket it hashes to).
- apples 7 finds the existing "apples" node already in the
tableand updates itsvalueto 7, without allocating a new node.
Looking up "apples" finds the node and reports its current value, 7, so the program prints found, value 7.
Edge cases to consider
- a lookup in an empty
table(miss) - two keys that hash to the same bucket (a collision)
- inserting a
keythat already exists (update vs duplicate) - freeing every bucket chain without leaks
Common mistakes
- not handling collisions, so a second
keyoverwrites the first bucket slot - a hash function that ignores part of the
keyand clusters badly - leaking chain nodes when freeing the
table
Optional extension challenge
Add automatic resizing/rehashing when the load factor grows too large, to keep chains short.
You can re-fetch the starter code for this question: prac_q111.c.
Question 112Simulated Library ADT
Estimated time: 35-50 minutes
Note prac_q112.c uses the following data type:
struct book {
char title[100];
int year;
};
struct catalogue {
struct book *books;
int num_books;
int capacity;
};A small community library wants a simple in-memory catalogue system to track which books it owns, that can grow to hold any number of books without the programmer having to guess a fixed maximum size up front. You are to implement a small library-catalogue ADT backed by a dynamically resized array of book structs (provided in the starter header, along with a struct catalogue holding a pointer to the array, the current number of books, and the currently allocated capacity).
Write a function that adds a new book to the catalogue, growing the underlying array with realloc (doubling its capacity) whenever it is full, and a function that removes the book at a given index, shifting subsequent books down by one position to keep the array contiguous.
Your task is to complete the functions void catalogue_add(struct catalogue *cat, struct book b) and void catalogue_remove(struct catalogue *cat, int index), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q112.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q112.c:
gcc -Wall -Wextra -o prac_q112 prac_q112.c
./prac_q112
CProgramming 1978 TheCppBook 1985 REMOVE 0
1 book(s) remaining: TheCppBook
./prac_q112
TheCppBook 1985 REMOVE 0
0 book(s) remaining
./prac_q112
a 1 b 2 c 3 d 4 e 5 REMOVE 0
4 book(s) remaining: b c d eAssumptions / Restrictions / Clarifications
- A new, empty catalogue has
num_books == 0andcapacity == 0, withbooks == NULL. catalogue_addmust userealloc(not justmalloca huge array up front).- When the array needs to grow, its capacity should double (starting from an initial capacity of 1 when going from empty to non-empty).
indexpassed tocatalogue_removeis always valid (0 <= index && index < num_books).catalogue_removemust not leave a gap in the array; subsequent books must be shifted down by one position.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- designing a multi-function ADT
- dynamic array management of book records
- inserting and removing items by
index
Worked example
For example:
For adding CProgramming (1978) and TheCppBook (1985), then removing index 0:
catalogue_add(CProgramming):capacitygrows from 0 to 1,num_booksbecomes 1.catalogue_add(TheCppBook): the array is full (1 of 1), socapacitydoubles to 2,num_booksbecomes 2.catalogue_remove(0): TheCppBook (index1) is shifted down intoindex0,num_booksbecomes 1.
main prints "1 book(s) remaining: TheCppBook".
Edge cases to consider
- adding to an empty catalogue
- removing the first or last book
- removing the only remaining book from the catalogue
- resizing the catalogue when capacity is reached
Common mistakes
- forgetting to shift subsequent elements left on remove
- not freeing book title strings when removing or destroying catalogue
- off-by-one when checking catalogue bounds
Optional extension challenge
Add an author field to struct book and a function int catalogue_count_by_author(...).
You can re-fetch the starter code for this question: prac_q112.c.
Question 113CSV Inventory Report
Estimated time: 45-60 minutes
Note prac_q113.c uses the following data type:
struct item {
char name[50];
int quantity;
double unit_price;
};A stock-control tool imports inventory from a CSV file exported by a supplier. Each line of the file is formatted as name,quantity,price (for example Widget,10,2.50), with no spaces around the commas. Write a function that reads such a file, computes the total value of the inventory (the sum over all items of quantity times price), returns that total, and reports how many items were read via an output parameter.
The struct item type used to hold each record is provided in the starter header.
Your task is to complete the function double inventory_value_from_file(char *filename, int *out_count), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q113.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q113.c:
gcc -Wall -Wextra -o prac_q113 prac_q113.c
./prac_q113
stock.csv
3 items, total value 75.00
./prac_q113
missing.csv
0 items, total value -1.00Assumptions / Restrictions / Clarifications
- If the file cannot be opened, set
*out_countto 0 and return -1.0. - Each line is well-formed as
name,quantity,pricewith no surrounding whitespace; the name is at most 49 characters. - The total value is the sum of
quantity * unit_priceover every item. - An empty file has 0 items and a total of 0.0.
- Remember to close the file before returning.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- file handling with
fopen/fscanf - parsing structured CSV fields
- accumulating a floating-point total into a struct-based record
Worked example
For example:
For stock.csv containing "Widget,10,2.50\nGadget,3,15.00\nBolt,100,0.05\n":
- Widget contributes 10 * 2.50 = 25.00.
- Gadget contributes 3 * 15.00 = 45.00.
- Bolt contributes 100 * 0.05 = 5.00.
- the total is 75.00 across 3 items, so the program prints "3 items, total value 75.00".
Edge cases to consider
- a file that cannot be opened (return -1.0, count 0)
- an empty file (0 items, total 0.0)
- an item with quantity 0
- prices that accumulate floating-point rounding
Common mistakes
- not checking
fopenforNULL - using
%dfor the price instead of%lf - forgetting the leading space in the
fscanfformat, so newlines break parsing
Optional extension challenge
Also identify and report the single most valuable line item (largest quantity * price) via extra output parameters.
You can re-fetch the starter code for this question: prac_q113.c.
Question 114Custom Dynamic Array ADT with Iterators
Estimated time: 40-55 minutes
Note prac_q114.c uses the following data type:
struct vec {
double *data;
int count;
int capacity;
};A scientific data-logging tool needs a growable list of double-precision sensor readings that can be appended to continuously and occasionally have erroneous readings removed from the middle. Implement a resizable dynamic array ADT of doubles, struct vec (see the provided starter header, storing a heap-allocated double array, a count of used elements, and an allocated capacity).
Write a function that creates a new empty vec, a function that pushes a new double onto the end (growing capacity by doubling, using realloc, starting from an initial capacity of 1 when empty), a function that removes and returns the value at a given index (shifting later elements down to close the gap), and a function that frees all memory owned by the vec.
Your task is to complete the functions struct vec vec_new(void), void vec_push(struct vec *v, double value), double vec_remove_at(struct vec *v, int index) and void vec_free(struct vec *v), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q114.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q114.c:
gcc -Wall -Wextra -o prac_q114 prac_q114.c
./prac_q114
1.5 2.5 3.5 REMOVE 1
removed 2.5
vector is now: 1.5 3.5
./prac_q114
9.0 REMOVE 0
removed 9
vector is now:
./prac_q114
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 31.0 32.0 33.0 34.0 35.0 36.0 37.0 38.0 39.0 40.0 41.0 42.0 43.0 44.0 45.0 46.0 47.0 48.0 49.0 50.0
count 50Assumptions / Restrictions / Clarifications
indexpassed tovec_remove_atis always valid (0 <= index && index < v->count).vec_pushmust never overflow the allocated capacity; grow before writing if the array is full.vec_freemust setv->datatoNULLandv->countandv->capacityto 0 after freeing.- None of these functions may use a fixed-size array; all storage must be dynamically allocated and resized as needed.
- A newly created vec (from
vec_new) has count == 0 and capacity == 0, with data ==NULL.
Stuck? Here's a hint
What this practises
This question gives you practice with:
realloc-based growable arrays- amortised append performance
- removing elements and maintaining dense contiguous storage
Worked example
For example:
For pushing 1.5, 2.5, 3.5, then removing index 1:
- after the three pushes, the vec holds [1.5, 2.5, 3.5],
count3. vec_remove_at(1)savesdata[1] = 2.5, shiftsdata[2] = 3.5down intoindex1, and decrementscountto 2.
main prints "removed 2.5" then "vector is now: 1.5 3.5".
Edge cases to consider
- appending to a vector at capacity
- removing the head or tail element
- removing the single remaining element
- freeing an empty vector
Common mistakes
- confusing size and capacity, overflowing the allocated buffer
- forgetting to shift elements on remove
- not freeing the backing array in
vec_free
Optional extension challenge
Add vec_insert_at(struct vec *v, int index, int value) which shifts elements to the right to insert.
You can re-fetch the starter code for this question: prac_q114.c.
Question 115Multi-Level Undo Graph Cycle Check
Estimated time: 60-75 minutes
A build system (similar to make) needs to detect circular dependencies between build targets before attempting a build, since a cycle would make it impossible to determine a valid build order. A directed dependency graph of n build targets (numbered 0 .. n - 1) is given as an n x n adjacency matrix, where matrix[i][j] == 1 means target i depends on target j (j must be built before i).
Write a recursive function, using depth-first search with a 3-state colouring scheme (unvisited / in-progress / done, using an auxiliary state array you declare inside your function), that determines whether the dependency graph contains a cycle (which would make the targets impossible to build in any valid order). Return 1 if a cycle exists, 0 otherwise.
Your task is to complete the function int has_cycle(int matrix[20][20], int n), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q115.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q115.c:
gcc -Wall -Wextra -o prac_q115 prac_q115.c
./prac_q115
4 0-1 1-2 2-0
1
./prac_q115
4 0-1 0-2 1-3
0
./prac_q115
1 0-0
1Assumptions / Restrictions / Clarifications
nis at most 20, matching the fixed array size.- The graph may be disconnected; your search must check every vertex, not just those reachable from vertex 0.
- A self-loop (
matrix[i][i] == 1) counts as a cycle. - The 3-state colouring (unvisited / in-progress / done) is required to correctly detect cycles in a directed graph; a simple visited/unvisited scheme is not sufficient.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- graph cycle detection
- DFS with three-colour marking
- recursion over dependencies
Worked example
For example:
For edges 0-1, 1-2, 2-0 (n = 4):
- the search starts at vertex 0, marking it
IN_PROGRESS. - it follows
0->1, marking 1IN_PROGRESS, then follows1->2, marking 2IN_PROGRESS. - starting at 2, it follows
2->0, and finds 0 is stillIN_PROGRESS(not yetDONE).
This back-edge to an in-progress vertex means a cycle exists, so has_cycle returns 1.
Edge cases to consider
- a graph with no edges (no cycle)
- a self-loop (a node depending on itself)
- a long dependency chain with no cycle
- a cycle buried deep in the graph
Common mistakes
- using a plain visited flag that cannot tell a cross-edge from a genuine back-edge
- not resetting the in-progress mark when a DFS branch completes
- missing cycles that do not involve the start node
Optional extension challenge
Produce a full topological ordering when the graph is acyclic, and report the nodes on the cycle when it is not.
You can re-fetch the starter code for this question: prac_q115.c.
Question 116Conway's Game of Life
Estimated time: several hours
Implement one generation of Conway's Game of Life on a fixed rows by cols board. Each cell is either alive (*) or dead (.). Cells beyond the edge of the board are treated as permanently dead.
The rules, applied to every cell simultaneously based on its eight neighbours: a live cell with two or three live neighbours stays alive, otherwise it dies (loneliness or overcrowding); a dead cell with exactly three live neighbours becomes alive; every other cell stays dead. Write life_step, which advances the whole board by one generation in place.
The crucial subtlety is simultaneity: every cell's new state depends on the OLD neighbour counts, so you must compute the entire next generation into a separate buffer before copying it back. The provided main reads the board and a step count, runs life_step that many times, and prints the final board.
Your task is to complete the function void life_step(char **grid, int rows, int cols), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q116.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q116.c:
gcc -Wall -Wextra -o prac_q116 prac_q116.c
./prac_q116
5 5 1
.....
.....
.***.
.....
.....
.....
..*..
..*..
..*..
.....
./prac_q116
5 5 2
.....
.....
.***.
.....
.....
.....
.....
.***.
.....
.....
./prac_q116
4 4 5
.**.
.**.
....
....
.**.
.**.
....
....Assumptions / Restrictions / Clarifications
- Neighbours are the eight surrounding cells; cells off the board count as dead.
- All cells update simultaneously from the previous generation, not one at a time.
- A live cell is the character
*; a dead cell is.. grid[r]is a null-terminated string of lengthcols; keep it null-terminated.- Do not print inside
life_step; onlymainprints the board.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- simultaneous update using a double buffer
- counting neighbours with bounds checks
- managing a dynamically allocated 2D board
- translating a rule set into clean conditional logic
Worked example
For example:
For the 5x5 blinker (three cells in a horizontal row) run for 1 step:
- the two end cells each have only one live neighbour, so they die.
- the centre cell has two live neighbours and survives.
- the cells directly above and below the centre each have three live neighbours and are born.
so the row flips to a vertical bar; after 2 steps it returns to horizontal (period 2). A 2x2 block never changes.
Edge cases to consider
- cells on the border, whose off-board neighbours are dead
- a still life such as a 2x2 block (unchanged every step)
- an oscillator such as the blinker (period 2)
- an empty board staying empty
- running for zero steps (board printed unchanged)
Common mistakes
- updating cells in place so later cells see already-updated neighbours
- counting the cell itself as one of its neighbours
- reading neighbours off the edge of the board without bounds checks
- leaking the temporary next-generation buffer each step
Optional extension challenge
Make the board toroidal (edges wrap around), and detect when the pattern becomes static or starts repeating a previous generation, stopping early.
You can re-fetch the starter code for this question: prac_q116.c.
Question 117RPN Calculator with Variables
Estimated time: several hours
Build the evaluation core of a small stack-based reverse-Polish-notation (RPN) calculator that also supports 26 single-letter variables a through z. Write rpn_eval, which evaluates one space-separated RPN expression.
Each token in expr is either an integer literal, a single lowercase letter naming a variable (whose current value is read from vars), or one of the binary operators +, -, *, /, %. Operators pop the top two values (the second popped is the left operand), and push the result.
A well-formed expression leaves exactly one value on the stack, which you return. If anything goes wrong -- an unknown token, a stack underflow, a division or modulo by zero, or leftover values on the stack -- set *error to 1 and return 0. On success set *error to 0.
The provided main reads lines from standard input. A line of the form x = <expr> evaluates <expr> and stores the result into variable x; any other line is evaluated and its result printed. Undefined variables read as 0.
Your task is to complete the function long rpn_eval(const char *expr, long vars[26], int *error), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q117.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q117.c:
gcc -Wall -Wextra -o prac_q117 prac_q117.c
./prac_q117
x = 3 4 +
y = x 2 *
y x -
5 1 2 + 4 * + 3 -
x = 7
y = 14
7
14
./prac_q117
10 0 /
z
1 2 3 +
foo
error
0
error
errorAssumptions / Restrictions / Clarifications
- Tokens are separated by spaces; you may use
strtokon a local copy ofexpr. - The second value popped is the left-hand operand: for
6 2 -the result is 4, not -4. - Division and modulo by zero must set
*errorand must not crash. - An expression that does not reduce to exactly one value (too few or too many operands) is an
error. - Do not modify the string
exprin place if it is not your own copy; do not print insiderpn_eval.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- implementing a stack machine over tokenised input
- distinguishing literals, variable names and operators
- robust
errorhandling through an out-parameter - operand-order care for non-commutative operators
Worked example
For example:
For the session x = 3 4 +, y = x 2 *, y x -:
3 4 +pushes 3 then 4, then+pops them and pushes 7, soxbecomes 7.x 2 *pushes 7 (the value ofx) then 2, then*gives 14, soybecomes 14.y x -pushes 14 then 7, then-computes 14 - 7 = 7.
The final bare expression 5 1 2 + 4 * + 3 - evaluates to 5 + (1+2)*4 - 3 = 14.
Edge cases to consider
- division or modulo by zero (must report
error, not crash) - an expression with leftover operands such as
1 2 3 + - an operator with too few operands such as
+ - reading an undefined variable, which is defined to be 0
- an unknown token such as
foo
Common mistakes
- swapping the operands so
6 2 -yields -4 instead of 4 - calling
strtokdirectly onexprand corrupting the caller's string - forgetting to check for an empty stack before popping two values
- not verifying that exactly one value remains at the end
Optional extension challenge
Add support for a unary neg operator and a dup token that duplicates the top of the stack, and allow multi-letter variable names using a small hash map.
You can re-fetch the starter code for this question: prac_q117.c.
Question 118Maze Solver (Shortest Path, BFS)
Estimated time: several hours
Given a rectangular grid maze, find the length of the shortest path from the start cell S to the exit cell E, moving only up, down, left or right and never through a wall #. Open cells are .. Write shortest_path, which returns the number of steps in the shortest route, or -1 if the exit is unreachable.
The natural algorithm is a breadth-first search from S: explore the maze in rings of increasing distance so that the first time you reach E you have found a shortest path. You will need your own queue (an array plus head and tail indices) and a visited/distance array indexed by row * cols + col.
The provided main reads the grid dimensions and rows from standard input, calls your function, and prints the result.
Your task is to complete the function int shortest_path(char **grid, int rows, int cols), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q118.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q118.c:
gcc -Wall -Wextra -o prac_q118 prac_q118.c
./prac_q118
5 5
S....
.###.
.#.#.
.#.#.
...#E
shortest path: 8 steps
./prac_q118
3 3
S#E
###
...
no path
./prac_q118
1 2
SE
shortest path: 1 stepsAssumptions / Restrictions / Clarifications
- Movement is 4-directional only; diagonal moves are not allowed.
- There is exactly one
Sand at most oneE; ifEis missing or walled off, return -1. - The start cell counts as distance 0; each move adds 1.
- Do not revisit a cell: mark distances as you enqueue to avoid infinite loops.
grid[r]is a null-terminated string of lengthcols.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- breadth-first search on an implicit
gridgraph - implementing a queue with a plain array
- mapping 2D coordinates to a 1D index
- distinguishing unreachable from reachable goals
Worked example
For example:
For the 5x5 maze whose only route snakes around the internal walls:
- BFS explores outward from
Sone ring at a time, so distances grow by one per move. - the first time the search dequeues
E, the recorded distance is guaranteed minimal. - here the shortest corridor is 8 moves long, so the answer is 8.
The blocked 3x3 maze S#E / ### / ... has walls sealing E off, so the answer is no path.
Edge cases to consider
Sadjacent toE(answer 1)Ecompletely walled off (answer -1 / no path)- a maze with no
Eat all - only one legal route, forcing a long detour
- avoiding revisiting cells so the search terminates
Common mistakes
- using depth-first search, which finds a path but not the shortest one
- marking a cell visited only when dequeuing, allowing duplicates and blow-up
- walking off the edge of the
gridwithout bounds checks - treating diagonal neighbours as reachable
Optional extension challenge
Reconstruct and print the actual shortest path (as a sequence of moves) by storing a parent for each visited cell, and support a W weight tile that costs 2 to enter using Dijkstra's algorithm.
You can re-fetch the starter code for this question: prac_q118.c.
Question 119Resizable Hash Map (Chaining, Resize, Delete)
Estimated time: several hours
Note prac_q119.c uses the following data type:
struct hnode {
char *key;
int value;
struct hnode *next;
};
struct hmap {
struct hnode **buckets;
int nbuckets;
int size;
};
// Provided for you in the starter file:
// struct hmap *hmap_create(void);
// unsigned long hash_str(const char *s); // djb2
// void hmap_resize(struct hmap *m, int new_n); // rehash into new_n bucketsImplement the three core operations of a production-style hash map with string keys and integer values. Collisions are handled by separate chaining (a linked list per bucket), and the table automatically grows and rehashes when it becomes too full.
You are given the struct hmap and struct hnode types plus the provided helpers hmap_create, hash_str (the djb2 hash) and hmap_resize. Your job is to write hmap_put, hmap_get and hmap_delete.
hmap_put updates the value if the key already exists, otherwise inserts a new node at the head of the correct bucket; after an insertion, if the number of stored keys exceeds the number of buckets, print [resize <old> -> <new>] and call hmap_resize to double the table. hmap_get returns the value and sets *found; hmap_delete unlinks and frees the matching node, returning 1 if a key was removed and 0 otherwise.
Your task is to complete the functions void hmap_put(struct hmap *m, const char *key, int value), int hmap_get(struct hmap *m, const char *key, int *found) and int hmap_delete(struct hmap *m, const char *key), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q119.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q119.c:
gcc -Wall -Wextra -o prac_q119 prac_q119.c
./prac_q119
put apple 1
put banana 2
put cherry 3
put date 4
put fig 5
get cherry
size
put apple = 1
put banana = 2
put cherry = 3
put date = 4
[resize 4 -> 8]
put fig = 5
get cherry -> 3
size = 5
./prac_q119
put x 1
get y
del x
get x
del x
size
put x = 1
get y -> (not found)
del x -> ok
get x -> (not found)
del x -> absent
size = 0Assumptions / Restrictions / Clarifications
- Bucket index is
hash_str(key) % m->nbuckets; recompute it after any resize. - Copy the
keywithstrdupwhen inserting, andfreeit (and the node) on delete. - Keep
m->size(number of stored keys) accurate across inserts, updates and deletes. - Grow when
m->size > m->nbuckets, doubling the bucket count and rehashing every node. - Deleting an absent
keymust return 0 and change nothing.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- separate chaining with per-bucket linked lists
- growing and rehashing a table on demand
- pointer-to-pointer deletion from a linked list
- owning heap memory for keys and nodes
Worked example
For example:
For the commands put apple 1 ... put date 4 then put fig 5:
- the map starts with 4 buckets; inserting the 5th distinct
keymakessize(5) exceednbuckets(4). hmap_puttherefore prints[resize 4 -> 8]and rehashes every node into 8 buckets.get cherrystill finds itsvalueafterwards because rehashing preserves all keys.
Edge cases to consider
- updating an existing
key(size must not change) - getting a
keythat is not present (*found= 0) - deleting an absent
key(returns 0, changes nothing) - inserting enough distinct keys to trigger a resize
- deleting the only
keyin a bucket
Common mistakes
- reusing a stale bucket index after a resize moved everything
- forgetting to
freethe duplicatedkeystring on delete, leaking memory - incrementing
sizewhen merely updating an existingkey - losing the rest of a chain by mis-linking during deletion
Optional extension challenge
Add hmap_free that releases every node and key, and shrink the table (halve and rehash) when the load factor drops below one quarter.
You can re-fetch the starter code for this question: prac_q119.c.
Question 120Tiny Regex Matcher (. and *)
Estimated time: several hours to a full day
Implement a miniature regular-expression matcher, the classic interview-hard problem popularised by Rob Pike. Write regex_match, returning 1 if pattern matches text and 0 otherwise.
The pattern language has four features: an ordinary character matches itself; . matches any single character; c* matches zero or more consecutive copies of the preceding element (a literal or .); a leading ^ anchors the match to the start of the text; and a trailing $ anchors it to the end.
Without a leading ^, the pattern may match anywhere inside text, so a*b matches xxaab. The elegant solution is a pair of mutually recursive helper functions; think carefully about how * tries progressively longer matches while still allowing the rest of the pattern to succeed.
Your task is to complete the function int regex_match(const char *pattern, const char *text), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q120.c also contains a main function which allows you to test your function. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your function and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your function will be called directly during marking. The main function is only to let you test your function.
Here is how the main function allows you to test prac_q120.c:
gcc -Wall -Wextra -o prac_q120 prac_q120.c
./prac_q120
a*b aaab
c.t cat
x.*z xyyyz
^ab abcd
a*b ~ "aaab": match
c.t ~ "cat": match
x.*z ~ "xyyyz": match
^ab ~ "abcd": match
./prac_q120
a*b b
ab$ crab
a*b ac
c.t ct
^ab bab
a*b ~ "b": match
ab$ ~ "crab": match
a*b ~ "ac": no match
c.t ~ "ct": no match
^ab ~ "bab": no matchAssumptions / Restrictions / Clarifications
.matches exactly one character; it does not match the end of the string.*always binds to the single element immediately before it.^is only special as the very first character of thepattern;$only as the very last.- The empty
patternmatches any text (it matches the empty prefix). - Do not use any library regex functions; implement the matching yourself.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- mutual recursion over two strings
- backtracking implicit in the
*case - reasoning about base cases and anchors
- pointer walking through C strings
Worked example
For example:
For a*b against aaab:
a*can match zero or moreacharacters; the matcher tries the longest run first.- after consuming
aaa, the remainingpatternbmatches the finalb, so the answer is match.
For ab$ against crab, the $ forces ab to sit at the very end, which it does, so it matches; but a*b against ac fails because no arrangement of a* leaves a b to match.
Edge cases to consider
a*bmatchingb(the star matches zero characters)- the anchor
$requiring the text to end exactly - the empty
pattern, which matches everything .refusing to match past the end of the text- a
patternthat only matches partway through an unanchored text
Common mistakes
- treating
*as matching the character after it instead of the one before it - an infinite loop in the
*case from never advancing the text pointer - forgetting that
.must still consume exactly one real character - mishandling
^/$when they appear (or do not appear) at the ends
Optional extension challenge
Add support for + (one or more) and ? (zero or one), and a character class [...] such as [a-z].
You can re-fetch the starter code for this question: prac_q120.c.
Question 121LRU Cache (Hash Map + Doubly Linked List)
Estimated time: a full day
Note prac_q121.c uses the following data type:
#define NBUCKETS 16
struct entry {
int key;
int val;
struct entry *hnext; // bucket chain
struct entry *prev, *next; // recency list (head = most recent)
};
struct lru {
int capacity;
int size;
struct entry *buckets[NBUCKETS];
struct entry *head, *tail; // doubly linked recency list
};
// Provided for you in the starter file:
// struct lru *lru_create(int capacity);
// unsigned bucket_of(int key);
// void list_unlink(struct lru *c, struct entry *e);
// void list_push_front(struct lru *c, struct entry *e);
// void bucket_remove(struct lru *c, struct entry *e);Implement a fixed-capacity Least-Recently-Used (LRU) cache mapping integer keys to integer values. This is a canonical hard interview problem because it demands O(1) lookup AND O(1) recency updates simultaneously, achieved by combining a hash table (bucket chains) with a doubly linked list ordered from most- to least-recently-used.
You are given the structures and all the plumbing: lru_create, the bucket hashing, and the doubly-linked-list helpers list_unlink, list_push_front and bucket_remove. You must write lru_get and lru_put.
lru_get returns the value for a key and moves that node to the front of the recency list, or returns -1 on a miss. lru_put updates an existing key (moving it to the front); on a genuinely new key, if the cache is full it must evict the tail node (the least recently used), printing evict <key>, before inserting the new node at the front.
Your task is to complete the functions int lru_get(struct lru *c, int key) and void lru_put(struct lru *c, int key, int val), whose behaviour is described above. This is the only code you need to write; it will be called directly during marking.
Testing
prac_q121.c also contains a main function which allows you to test your functions. It reads its input from standard input — exactly the lines shown being typed in the session below — then calls your functions and prints the result. You type the input directly at the keyboard (or redirect a file into the program); nothing is passed on the command line.
Do not change this main function. If you want to change it, you have misread the question.
Your functions will be called directly during marking. The main function is only to let you test your functions.
Here is how the main function allows you to test prac_q121.c:
gcc -Wall -Wextra -o prac_q121 prac_q121.c
./prac_q121
2
put 1 10
put 2 20
get 1
put 3 30
get 2
get 3
get 1 -> 10
evict 2
get 2 -> miss
get 3 -> 30
./prac_q121
2
put 5 50
get 9
put 6 60
put 7 70
get 5
put 6 66
get 6
get 9 -> miss
evict 5
get 5 -> miss
get 6 -> 66Assumptions / Restrictions / Clarifications
- Every access (
gethit, orputthat updates or inserts) makes thatkeythe most recently used. - Eviction removes the tail of the recency list, which is the least recently used
key. lru_geton a missingkeyreturns -1 without changing the cache.- Keep the hash bucket chain and the recency list consistent: a node lives in both at once.
- Use the provided helpers rather than re-deriving the pointer surgery.
Stuck? Here's a hint
What this practises
This question gives you practice with:
- combining a hash table with a doubly linked list for O(1) operations
- maintaining recency order under reads and writes
- eviction of the least recently used element
- keeping two data structures pointing at the same nodes consistent
Worked example
For example:
For capacity 2 and the commands put 1 10, put 2 20, get 1, put 3 30:
- after the two puts the recency order (most to least recent) is 2, 1.
get 1returns 10 and promoteskey1, making the order 1, 2.put 3 30needs room, so it evicts the least recently usedkey2 (printingevict 2) and inserts 3.- a later
get 2therefore reports a miss.
Edge cases to consider
- a
getmiss on akeynever inserted (returns -1) - updating an existing
key's value (no eviction, but it becomes most recent) - a
putthat evicts because the cache is exactly full - re-inserting a
keythat was previously evicted - capacity interacting with which
keyis considered least recently used
Common mistakes
- evicting on an update instead of only on a genuinely new
key - forgetting to remove the evicted node from its hash bucket as well as the list
- not promoting a node to the front on a
gethit - reading a freed node after eviction
Optional extension challenge
Add an lru_free that releases every node, and support a del <key> command that removes a key from both the bucket chain and the recency list.
You can re-fetch the starter code for this question: prac_q121.c.