Algorithm Efficiency Problem Set II - Solutions

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 5

COMP 182

Algorithm Efficiency
Solutions

1. Consider the pseudo-code of Algorithm Mystery1.

Algorithm 1: Mystery1.
Input: Array A[0..n − 1] of real numbers.
Output: None.
Modifies: A.
i ← 0; j ← n − 1;
while i ≤ j do
if A[i] < 0 then
i ← i + 1;
else
t ← A[i]; A[i] ← A[j]; A[j] ← t;
j ← j − 1;

(a) Consider array A = [2, −1.5, 3, 0, 1]. What are the values in array A after Algorithm Mystery1 runs on
it?
After running the algorithm, A becomes [−1.5, 3, 0, 1, 2].
(b) Given an array A, what does Algorithm Mystery1 do?
The algorithm rearranges the array so that all the negative numbers appear first in the array.
(c) Using big-O notation, what is the worst-case running time of algorithm Mystery1? Explain your answer.
This algorithm takes O(n). In every iteration of the while loop, either i increases or j decreases. There-
fore, the loop performs O(n) iterations. Each iteration performs O(1) operations. Therefore, the algorithm
takes a total of O(n) time.

2. For Algorithm Mystery2, which of the following gives the tightest bound on the running time in the worst case:
O(n), O(n log n), or O(n2 )? Explain your answer.

Algorithm 2: Mystery2
Input: Natural number n.

1 i ← 1;
2 while i < n do
3 j ← 1;
4 while j < n do
5 Print “Hello World”;
6 j ← j · 5;
7 i ← i + 1;

1
COMP 182 Algorithm Efficiency

Lines 1, 3, 5, 6, and 7 take O(1) steps each.


The outer loop on Line 2 takes O(n) time, and the inner loop on Line 4 iterates O(log5 n) time. Therefore, this
algorithm takes O(n log n).

3. Consider Algorithm Mystery3.

Algorithm 3: Mystery3
Input: Natural number n.
Output: ...

1 i ← 1;
2 j ← n − 1;
3 p ← j;
4 while (p 6= n ∧ i < j) do
5 i ← i + 1;
6 p ← p + j;
7 while p > n do
8 j ← j − 1;
9 p ← p − i;

10 if p = n then
11 return i;
else
12 return n;

(a) What does Algorithm Mystery3 return for input n? Explain your answer, by discussing how the algorithm
computes the value. Hint: Think about what value p holds throughout the algorithm.
The algorithm computes the smallest divisor of n that is greater than 1. Observe that p = i · j: To maintain
this “invariant,” every time i is incremented by 1, p is incremented by j (Lines 5-6), and every time j is
decremented by 1, p is decremented by i (Lines 8-9). So, starting with i = 2, the algorithm checks if
there’s a j such that i · j = n by repeatedly decreasing the value of j; that is, it checks if i is a divisor of
n. if not, it increments i by 1 and checks if this i is a divisor, and so on. If the algorithm finds such an i, it
returns it; otherwise, it returns the number n (which would happen in the case of prime numbers).
(b) Which of the following
√ gives the tightest bound on the running time of Algorithm Mystery3 in the worst
case: O(n), O(n n), or O(n2 )? Explain your answer.
• The loosest analysis would state this is O(n2 ), as one would say the loop on Line 4 takes O(n)
iterations and the nested loop on Line 7 takes O(n) iterations, and all other lines take O(1).

• A better analysis would result in O(n n). Given √ how the algorithm works
√ and that p = i · j is
invariant throughout the algorithm, once i reaches n, √ j gets to about n and the outer loop (on
Line 4) stops, which means the loop on Line 4 takes O( n). In the worst case, the loop on Line √ 7
takes O(n) iterations, and j cannot decrease for more than n steps. This analysis gives the O(n n)
analysis.
• The tightest analysis yields O(n). Notice that j starts with value n − 1 and in every iteration of the
inner loop, it decrements by 1, and it never increases.
√ Therefore, the inner loop takes a total of O(n)
across all runs of that loop. Lines 5 and 6 take O( n) across all iterations of the outer loop. In other
words, think of this analysis as the time taken for Lines 4–6 plus the time takes for Lines 4, 7–9, the

former of which
√ takes O( n) and the latter of which takes O(n) time. Therefore, the total time is
O(n) + O( n) = O(n).

4. In this problem, we consider a sequence of numbers x0 , x1 , . . . , xn−1 to form a Fibonacci series if xi = xi−1 +
xi−2 for every i ≥ 2 and xi ≤ xi+1 for every 0 ≤ i ≤ n − 1 (there is no condition on what the values of x0
and x1 are). Given an array L of n positive integers, where n ≥ 3, we are interested in knowing whether the

2
COMP 182 Algorithm Efficiency

Algorithm 4: Fibonacci.
Input: Array L[0..n − 1] of positive integers (n ≥ 3).
Output: T rue if L is a permutation of n numbers that form a Fibonacci series; F alse otherwise.
1 MergeSort(L);
2 f lag ← T rue;
3 for i ← 2 to n − 1 do
4 if L[i] 6= L[i − 1] + L[i − 2] then
5 f lag ← F alse;
6 Break;

7 return f lag;

elements of L are a permutation of a Fibonacci series of n numbers. Give the pseudo-code of an O(n log n)
algorithm for solving this problem (Assume, for example, that the smallest two elements in L are 1 and 2).
MergeSort takes O(n log n) and the rest of the algorithm takes O(n). Therefore, this algorithm takes
O(n log n).
5. Let Count(A, x) be a linear-time function that returns the number of times that element x appears in array A.
Consider Algorithm Mystery below.

Algorithm 5: Mystery
Input: Array A[0..n − 1] of n numbers.
Output: ...

1 if n = 1 then
2 return A[0];
3 else
4 a ← Mystery(A[0..bn/2c − 1]);
5 b ← Mystery(A[bn/2c..n − 1]);
6 if Count(A, a) > bn/2c then
7 return a;
8 if Count(A, b) > bn/2c then
9 return b;
10 return N ONE;

(a) Given an array A[0..n − 1], what does Algorithm Mystery compute and return? Explain your answer.
The algorithm tests if there is a “majority” element in A—that is, an element that more than half the
elements in A equal. If so, it returns it; otherwise, it returns N one. The algorithm makes use of the
following observation:
• If more than half of the elements in A equal some number x, then either more than half the elements
in the left half of A equal x, or more than half of the elements in the right half of A equal x.
So, the algorithm finds if such an element exists in the left or right half. If one exists, the algorithm counts
its number of occurrences in the entire array to see if it exceeds half n.
(b) Using big-O notation, what is the worst-case running time of Algorithm Mystery? Clearly indicate the
input size and explain how you arrived at the running time.
The recurrence for the running time is

T (n) = 2T (n/2) + O(n).

So, for the Master Theorem, we have a = 2, b = 2, and d = 1. Since a = bd , we get that the running time
is O(n log n).

3
COMP 182 Algorithm Efficiency

6. A binary tree T is a finite set of nodes that is either empty or consists of a root and two disjoint binary trees TL
and TR called, respectively, the left and right subtree of the root. The following is a binary tree whose root is
the node labeled 0.

1 2

3 4 5

(a) The height of a binary tree is the length of a longest path from the root to a leaf. Give the pseudo-code of
an O(n) recursive algorithm for computing the height of a binary tree with n nodes.
Here’s a recursive algorithm ComputeHeight for computing the height of a binary tree T :

Algorithm 6: ComputeHeight.
Input: Binary tree T .
Output: The height of T .
if (T = ∅) or (TL = TR = ∅) then
return 0;
else
return (1 + max{Height(TL ), Height(TR )});

This algorithm takes O(n) time since it visits all the nodes in the tree a constant number of times each.
(b) Three standard ways of traversing a binary tree are: (1) preorder traversal, where the root is visited first,
then the left subtree, and then the right subtree; (2) inorder traversal, where the root is visited after the left
subtree but before the right subtree; and, (3) postorder traversal, where the root is visited after the left and
right subtrees (in that order). For example, the pre-, in-, and postorder traversals of the binary tree above
are, respectively: (1) 0, 1, 3, 6, 4, 2, 5; (2) 3, 6, 1, 4, 0, 5, 2; and (3) 6, 3, 4, 1, 5, 2, 0. Give the pseudo-code
of recursive algorithms for traversing a binary tree in all three ways, and analyze the running times of your
algorithms. In such algorithms, and assuming the input is tree T , you can assume that T points to the
root of the tree, and that TL and TR point to the left and right children (when they exist) of the root of T ,
respectively. Furthermore, you can assume, for example, that `(T ) denotes the label (number) at the root
of T .
For a binary tree T , let us denote by `(T ) the label of the root of tree T . Here’s a recursive algorithm
Preorder for traversing a binary tree in a preorder fashion (it can be modified easily to do in- and postorder
traversals):

Algorithm 7: PreOrder.
Input: Binary tree T .
Output: None. (prints the nodes of the tree in preorder)
if T 6= ∅ then
Print `(T );
Preorder(TL );
Preorder(TR );

The algorithm takes O(n) time since it visits every node of the tree and performs a constant number of
iterations on each.
(c) Give the pseudo-code of a recursive algorithm for counting the number of leaves in a binary tree, and
analyze its running time.

4
COMP 182 Algorithm Efficiency

Algorithm 8: CountLeaves.
Input: Binary tree T .
Output: The number of leaves in T .
if (T = ∅) then
return 0;
else if TL = ∅ and TR = ∅ then
return 1;
else
return (CountLeaves(TL ) + CountLeaves(TR ));

Here’s a recursive algorithm CountLeaves for counting the number of leaves in a binary tree.
The algorithm takes O(n) time since it visits every node of the tree and performs a constant number of
iterations on each.

7. Given a sorted array of distinct integers A[0..n − 1], we want to check whether A[i] = i for some 1 ≤ i ≤ n − 1.
If such an i exists, return its value; otherwise, return −1.

(a) Give the pseudo-code of an O(log n) recursive algorithm for solving the problem.
Algorithm Problem7Algorithm is given below.

Algorithm 9: Problem7Algorithm.
Input: Sorted array A[0..n − 1] of distinct integers, and left/right boundaries l and r.
Output: An i for which A[i] = i, and −1 if such an i does not exist.
1 if l > r then
return −1;
2 m ← b(l + r)/2c;
3 if A[m] = m then
4 return m;
5 else if A[m] < m then
return Problem7Algorithm(A, m + 1, r);
else
return Problem7Algorithm(A, l, m − 1);

(b) Give a recurrence T (n) for the running time of your algorithm.

This is a divide-and-conquer algorithm that divides the problem into 2 sub-problems in every stage, solves
1 sub-problem, and takes only 1 step for the merge step. Therefore, we have
T (n) = T (n/2) + 7
T (1) = 7.
The constants here (7) may vary from one solution to another, depending on how the students count the
numbers of steps.
(c) Show that T (n) = O(log n).

We can use the Master Theorem. In our recurrence, we have a = 1, b = 2 and d = 0. Since a = bd , it
follows that T (n) = O(nd log n) = O(log n).

You might also like