25-Comp-A4 Program Design and Data Structures · May 2016
Question 8 of 8: Algorithm Design and Sorting — Deduplicating a Large Array
Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)
Notes on this paper
Paper format. 98-Comp-A4 Program Design and Data
Structures, May 2016 — 3 hours, closed book, no calculator
permitted. Eight questions of equal weight (20 marks each: some split as (a)
10 + (b) 10); candidates answer any five, so a complete paper is 100 marks.
Pseudocode or any high-level language is accepted, and the examiner's note
states explicitly that marking emphasises the operation of the program,
not syntactic details. All eight 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.
Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms,
4th ed. — tree traversals (ch. 12), sorting and Quicksort (ch. 7),
asymptotic analysis (ch. 3).
Weiss, Data Structures and Algorithm Analysis in C, 2nd ed. —
linked lists (ch. 3), binary search trees (ch. 4).
Deitel & Deitel, C++ How to Program, 10th ed. — class
design and templates (ch. 9–12), file streams (ch. 14).
Kernighan & Ritchie, The C Programming Language, 2nd ed. —
character/array I/O idioms (ch. 1, 7), pointers and structures (ch. 5–6).
Stroustrup, The C++ Programming Language, 4th ed. — class
templates and value semantics (ch. 3, 25–27).
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 8: Algorithm Design and Sorting — Deduplicating a Large Array (20 marks)
Given. An unsorted array $A$ of $n$ integers, possibly
containing duplicates, and two candidate dedupe strategies: (1) scan $A$ once,
copying each element into $B$ only if $B$ does not already contain it; (2)
Quicksort $A$ first, then apply the same scan-and-copy rule.
Find. Which method is faster for large $n$, with
justification (not a benchmark on a specific machine, but an asymptotic
argument).
Approach. Count worst-case comparisons for each method as
a function of $n$ and the number of duplicates, then compare growth rates;
confirm the argument by counting actual comparisons on the same synthetic
data for both methods at increasing $n$.
Analyze Method 1 without sorting. For each of the $n$
elements of $A$, checking "does it already exist in $B$" is a linear scan of
$B$, whose length grows as unique elements accumulate. In the worst case
(all elements distinct, so $B$ grows to length $n$), the total number of
comparisons is
$$0+1+2+\cdots+(n-1) = \frac{n(n-1)}{2} = O(n^2)$$
Even with many duplicates the bound stays $O(n^2)$ in the worst case, since
an adversarial input can still front-load $B$ with $\Theta(n)$ distinct
values before any duplicate appears.
Analyze Method 2 with a Quicksort pre-pass. Quicksort's
average-case running time is $O(n\log n)$ (its worst case is
$O(n^2)$ only for an adversarial pivot choice on already-sorted or reverse
data, which "an array of random integers" does not present). After sorting,
every duplicate is adjacent to its equal neighbours, so the scan-and-copy
becomes a single linear pass — compare each element only to the
previous one, an $O(n)$ operation, not $O(n)$ comparisons against a
growing $B$.
$$T_2(n) = \underbrace{O(n\log n)}_{\text{Quicksort}} + \underbrace{O(n)}_{\text{one linear scan}} = O(n\log n)$$
Compare growth rates. Since $O(n\log n)$ is strictly
smaller than $O(n^2)$ for all sufficiently large $n$ (formally,
$\lim_{n\to\infty} \frac{n\log n}{n^2} = 0$), Method 2 is asymptotically
faster whenever $n$ is "large," which is exactly the condition the question
poses — even though Method 2 does strictly more total work per
element for small $n$ (sorting overhead that a tiny array does not need to
pay back).
$$\boxed{\text{Method 2 (Quicksort, then one linear scan) is faster for large } n}$$
Confirm empirically by counting comparisons. Running
both methods on the same randomly generated arrays (with many repeated
values, matching "the array may contain duplicate integers") and counting
every comparison performed shows the predicted crossover: the two methods
are close at $n=200$, but Method 1's comparison count grows far faster than
Method 2's as $n$ increases to 2,000 and 20,000.
The ratio itself grows with $n$, which is the signature of two different
polynomial/log-linear growth rates rather than a fixed constant-factor
difference — confirming the $O(n^2)$ vs $O(n\log n)$ analysis above
rather than just an implementation-specific speed difference.
Question 8 — results
Quantity
Value
Method 1 (no sort) worst-case comparisons
$O(n^2)$, exactly $\tfrac{n(n-1)}{2}$ if all distinct