Tutorial1 Solutions
Tutorial1 Solutions
Tutorial 1 Solutions
Monday, Sep 20, 2021
1. If f (n) = O nlogb a− for some constant > 0, then T (n) = O nlogb a .
2. If f (n) = Θ nlogb a logk n for some constant k > 0, then T (n) = O nlogb a logk+1 n .
3. If f (n) = Ω nlogb a+ for some constant > 0 and f satisfies the regularity condition*, then
T (n) = O (f (n)).
(*Regularity condition: For some constant c < 1 and all sufficiently large n, a·f (n/b) 6 c·f (n).)
Note: There are recurrence relations which do not fall under any of these three cases (e.g. the
recurrence relation T (n) 6 T (n/5) + T (7n/10) + O(n) from QuickSelect where the smaller instances
√ √
are not of uniform size, or the recurrence relation T (n) 6 n · T ( n) + O(n) where a and b are
not constants). If you’re interested in how more general recurrences can be solved, there are some
excellent resources available online.12
Solution to Q1
f (n) = n log3 n.
1
http://jeffe.cs.illinois.edu/teaching/algorithms/notes/99-recurrences.pdf
2
http://web.csulb.edu/~tebert/teaching/lectures/528/recurrence/recurrence.pdf
3
Note that when proving an upper bound on the worst-case running time of an algorithm, you would encounter
equations of the form T (n) 6 . . . rather than T (n) = . . ., yielding T (n) = O(·) rather than T (n) = Θ(·). To derive a
lower bound, you need to explicitly construct instances on which the algorithm takes at least the claimed amount of
time.
1
(b) For T (n) 6 4T (n/2) + O(n2 ), we have:
f (n) = n2 .
f (n) = n log2 n.
Hence, again by case 2 of the Master theorem, T (n) = O(n log3 n).
f (n) = n0.5001 .
Solution to Q2
Let k = 1, and while f (k) > 0, double k. In at most dlog ne iterations, this will terminate as we
will have k > n. Let k ∗ be the value at which it terminates. Then, we know that k ∗ /2 < n 6 k ∗ .
We can binary-search n in this range (or for simplicity, in the range 1 . . . k ∗ ), as described by the
function FindFirstNonPositive below.
The running time for finding k ∗ is O(log n), and the running time for the subsequent binary search
is also O(log n). Hence, the overall running time is O(log n).
2
1 Function FindFirstNonPositive(A[1 . . . r]):
2 if r = 1 then
3 return A[1]
4 m ← br/2c
5 if A[m] 6 0 then
6 return FindFirstNonPositive(A[1 . . . m])
7 else
8 return FindFirstNonPositive(A[(m + 1) . . . r])
Solution to Q3
Suppose we have already calculated the maximum subarray sums in the two halves, A[1 . . . mid] and
A[mid +1 . . . n]. To find the overall maximum subarray sum, we need a third value: the maximum
subarray sum where the subarray overlaps both halves.
Note that this quantity is the sum of two quantities: the maximum suffix sum in the left half (i.e.
maximum sum of any A[i . . . mid]) and the maximum prefix sum in the right half (i.e. maximum
sum of any A[mid +1 . . . j]).
After recursively calling our algorithm on the two halves, we could spend O(n) time calculating
the maximum suffix sum in the left half and the maximum prefix sum in the right half. But as the
hint suggests, this will give us T (n) = 2 · T (n/2) + O(n), i.e., T (n) = O(n log n).
Instead, we want our recursive algorithm to return the maximum suffix sum in addition to the max-
imum subarray sum, when called on the left half, and return the maximum prefix sum in addition
to the maximum subarray sum, when called on the right half.
That means, our algorithm must return three quantities: maximum subarray sum, maximum prefix
sum, and maximum suffix sum. Note that the “parent” call must also return these quantities once
getting them from the two recursive calls, otherwise it is not a legitimate recursive algorithm.
When thinking along these lines, it becomes clear that to compute the maximum prefix and suffix
3
sums in the entire array, we will also need to know the sum of the left and right halves. Also, in
prefix and suffix sums, we need to allow empty prefix and suffix, which plays a key role in the first
step of the algorithm below.
Note that the worst-case running time of this algorithm is given by T (n) 6 2 · T (n/2) + O(1), which
yields T (n) = O(n).