25-Comp-A4 Program Design and Data Structures · December 2018
Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)
Paper format. 17-Comp-A4 Program Design and Data Structures, December 2018 — 3 hours, closed book, no calculator permitted. Nine questions, each of equal weight (Question 1 is split 10+10; Questions 2–9 are 20 marks each); candidates answer any six, and only the first six as they appear in the answer book are marked, so the paper is marked out of 120. Pseudocode or any high-level language (e.g. C or C++) is accepted, and the examiner's note states explicitly that marking emphasises the operation of the program, not syntactic details. All nine questions are answered below, because the whole set is the more useful revision resource. Answers are given in C or C++ as the question dictates; each is compilable as written, but a clear, correctly reasoned pseudocode answer would earn the same marks.
Reference texts for this subject.
The Computer Engineering citation list is built around architecture and networking texts (Patterson & Hennessy, Tanenbaum, Mano); this subject is programming and data structures, so the works above are cited instead.
Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.
Given. No numeric data; a design requirement for a templated class supporting empty and pre-populated construction, member addition, deletion and membership test, over any numeric element type.
Find. A Set<T> class, split into
Set.h (declaration) and Set.cc (implementation),
whose member functions never allow a duplicate element to be stored.
Approach. A class template Set<T>
wraps a dynamically resized array holding the current elements and a count
of how many are in use. add is the only place duplicates are
guarded against — every other operation (delete, membership test) is a
plain linear scan over that array. Assumption: mathematical
set operations beyond the three named (union, intersection, etc.) are out of
scope, since the question lists exactly addition, deletion and membership;
elements are compared with ==, which is why the class is
restricted to number types (as stated) rather than arbitrary objects.
std::vector.T*; omitting them lets the compiler-generated shallow copy
double-free the same buffer when a Set is copied.
// Set.h
#ifndef SET_H
#define SET_H
template <class T>
class Set {
public:
Set(); // empty set
Set(const T *elems, int n); // initialized with n elements
Set(const Set &other); // copy constructor
Set &operator=(const Set &other); // copy assignment
~Set();
void add(const T &value); // no-op if value already present
void remove(const T &value); // no-op if value not present
bool contains(const T &value) const;
int size() const { return count; }
private:
T *items;
int count;
int capacity;
void grow(); // doubles capacity
};
// The definitions live in Set.cc, which ends with explicit instantiations of
// Set<int>, Set<float> and Set<double> so that this header can be included on
// its own and Set.cc compiled as an ordinary separate translation unit.
#endifcontains, so the "no duplicates" invariant is enforced in
exactly one place.
// Set.cc
#include "Set.h"
template <class T>
Set<T>::Set() : items(nullptr), count(0), capacity(0) {}
template <class T>
Set<T>::Set(const T *elems, int n) : items(nullptr), count(0), capacity(0)
{
for (int i = 0; i < n; i++)
add(elems[i]); // add() rejects duplicates in the input too
}
template <class T>
Set<T>::Set(const Set &other)
: items(new T[other.capacity]), count(other.count), capacity(other.capacity)
{
for (int i = 0; i < count; i++) items[i] = other.items[i];
}
template <class T>
Set<T> &Set<T>::operator=(const Set &other)
{
if (this != &other) {
delete[] items;
capacity = other.capacity;
count = other.count;
items = new T[capacity];
for (int i = 0; i < count; i++) items[i] = other.items[i];
}
return *this;
}
template <class T>
Set<T>::~Set() { delete[] items; }
template <class T>
void Set<T>::grow()
{
int newCap = (capacity == 0) ? 4 : capacity * 2;
T *bigger = new T[newCap];
for (int i = 0; i < count; i++) bigger[i] = items[i];
delete[] items;
items = bigger;
capacity = newCap;
}
template <class T>
void Set<T>::add(const T &value)
{
if (contains(value)) return; // the ONE place duplicates are blocked
if (count == capacity) grow();
items[count++] = value;
}
template <class T>
void Set<T>::remove(const T &value)
{
for (int i = 0; i < count; i++) {
if (items[i] == value) {
items[i] = items[count - 1]; // swap-with-last: O(1), order not guaranteed
count--;
return;
}
}
}
template <class T>
bool Set<T>::contains(const T &value) const
{
for (int i = 0; i < count; i++)
if (items[i] == value) return true;
return false;
}
// Explicit instantiations for the number types this Set is required to
// support. These are what let Set.cc be compiled separately from Set.h: the
// compiler emits the member code for these three types here, so a translation
// unit that only sees Set.h still links. Adding a new element type means
// adding one more line below.
template class Set<int>;
template class Set<float>;
template class Set<double>;Set<int> s; // empty set
s.add(3); s.add(1); s.add(3); // second add(3) is a no-op: size() stays 2
s.contains(1); // true
s.remove(1);
s.contains(1); // false
int init[] = {2, 4, 2, 6};
Set<int> t(init, 4); // constructs {2, 4, 6}: duplicate 2 collapsed
$$\boxed{\text{size after } s.add(3);\,s.add(1);\,s.add(3); \;=\; 2}$$| Operation sequence | Resulting set / value |
|---|---|
| add(3); add(1); add(3) | {3, 1} — size 2 (duplicate rejected) |
| contains(1) then remove(1); contains(1) | true, then false |
| Set<int> t({2,4,2,6}, 4) | {2, 4, 6} — size 3 |
| Instantiated element types | int, float, double |