Review Lesson: Design and Analysis of
Computer Algorithms
Yanhui Li
Department of Computer Science and Technology,
Nanjing University
yanhuili@[Link]
November 20, 2024
Problem 1
We maintain a set S of real valued items that supports the following two
operations:
• Insert(S, x): insert item x into set S
• DeleteLargerHalf (S): delete the ⌈|S|/2⌉ largest valued items from S.
The Insert operation allows duplicate values to be inserted into S. So,
item values in S are not necessarily distinct. Briefly explain how to imple-
ment these two operations to run in O(1) amortized time.
Solution
We use linked-list to sort S, and insert new element at the head of the linked
list.
Amortized equation:
amortized cost = actual cost + accounting cost
Actual cost:
• Insert(S, x): 1
1
• DeleteLargerHalf (S): tn. find the n+1-⌈|S|/2⌉ smallest elements x in
S, and delete any element larger than or equal to x.
Accounting scheme:
• Insert(S, x): 2t
• DeleteLargerHalf (S): −tn
Therefore the amortized costs of two operations are given as follows:
• amortized cost of Insert(S, x)= 1 + 2t=O(1)
• amortized cost of DeleteLargerHalf (S)=0
We have to prove that the sum of the accounting cost is not less than 0 (i.e.,
positive). For any DeleteLargerHalf( ), assume S has n elements at this time
and the operation has −tn accounting cost. For the deleted elements x by
this operation, the number of them is larger than n/2, and each of them has
a 2t accounting cost when it is inserted. (Note that for any inserted element
x, it has at most one deletion in DeleteLargerHalf( ).) Therefore the total
accounting cost is not less than 0.
Problem 2
Solve the following recurrence relation exactly:
1 n=1
T (n) = 6 n=2 (1)
T (n − 2) + 3n + 4 n ≥ 3
2
Solution
T (2k + 1) = T (2(k − 1) + 1) + 3(2k + 1) + 4(k > 1)
= T (2(k − 2) + 1) + 3(2(k − 1) + 1) + 4 + 3(2k + 1) + 4
= ···
k
= T (1) + (3(2i + 1) + 4)
i=1
(k + 1)k
= 1 + 7k + 6
2
T (2k + 2) = T (2k + 2) + 3(2k + 2) + 4(k > 1)
= T (2(k − 2) + 2) + 3(2(k − 1) + 2) + 4 + 3(2k + 2) + 4
= ···
k
= T (2) + (3(2i + 2) + 4)
i=1
(k + 1)k
= 6 + 10k + 6
2
Problem 3
You are given a collection of n bolts of different widths and n corresponding
nuts. You are allowed to try a nut and bolt together, from which you can
determine whether the nut is larger or smaller than the bolt, or matches the
bolt exactly. However, there is no way to compare two nuts together or two
bolts together. The problem is to match each bolt to its nut. Design an
algorithm for this problem with average case efficiency in Ω(n log n).
Solution
Let wi and vi be the width of the i-th bolts and i-th nuts.
• Choose a bolts randomly and we assume that it is the x-th bolts with
width wx compare this bolts with all n nuts (n comparisons). Now the
n nuts are divided into three classes:
3
– N< = {i|vi < wx }
– N= = {i|vi = wx } Note that |N= | = 1, and we assume it is the
y-th nut with width vy = wx .
– N> = {i|vi > wx }
• Compare the y-th nut with all other n − 1 bolts, and divided them into
two classes (n − 1 comparisons):
– B< = {i|wi < vy }
– B< = {i|wi > vy }
• Note that for any nut in N< (N> ), its corresponding bolt is in B< (B> ).
And |N> | + |N< | = |B> | + |B< | = n − 1.
• Now, After 2n − 1 comparisons, we divide the total problem of size n
into two sub-problems with sizes |N> | and |N< |:
– match each bolt in B< to its nut in N< ;
– match each bolt in B> to its nut in N> ;
• We can solve this problem by above recursive steps. And the recursive
function is given as follows:
T (n) = 2n − 1 + T (|N> |) + T (|N< |)
For the random choosing, |N> | could be 0, 1, . . . , n − 1, and |N< | n − 1, n −
2, . . . , 0 correspondingly, with the probability 1/n. Therefore we can get the
recurrence function of T (n):
n−1
1
T (n) = { (T (i) + T (n − 1 − i))} + 2n − 1) (2)
n i=0
From
n−1
n−1
T (i) = T (n − 1 − i)
i=0 i=0
we can easily get that
n−1
2
T (n) = T (i) + 2n − 1
n i=0
By induction on n, we give the Inductive Proof of A(n) ∈ O(n ln n):
4
• Base case (n = 1) is trivial.
• Inductive assumption: A(i) ≤ c × i ln i for 1 ≤ i < n
• Inductive proof:
n−1
2
T (n) = T (i) + 2n − 1
n i=0
n−1
2
≤ c × i ln i + 2n − 1
n i=0
Note that
n−1
2 2c n
c × i ln i ≤ x ln xdx
n i=0 n 1
2c n2 ln n n2
≈ −
n 2 4
cn
= cn ln n −
2
Therefore,
c
T (n) ≤ cn ln n + n(2 − ) − 1
2
Let c = 4, we have A(n) ≤ 4n ln n.
The proof of T (n) ∈ Θ(n ln n) is similar. Here we give the conclusion:
T (n) ∈ Ω(n ln n) = Ω(n log n)
Problem 4
Given an undirected graph G = (V, E) and an edge e of G. Design an
algorithm to determine if there is a cycle in G containing e in time O(n + m).
Solution
Let e = (w, v) and E ∗ = E − {e}. Construct a new graph G∗ = (V, E ∗ ). Do
DFS to find whether w and v are in one connected component in G∗ .
5
Problem 5
Given a Directed Acyclic Graph G = (V, E), design a linear-time O(m + n)
algorithm to determine whether there is a directed path that visits each
vertex exactly once.
Solution
Let V = {v1 , v2 , . . . , vn }. Since G is a DAG, we can compute topological
numbers T op(i) and sort all vertices into the array T opsort[1, 2, . . . , n] in the
increasing order, which means vi = T opsort[T op(i)].
If there is a directed path
P ath = v1∗ , v2∗ , . . . , vn∗ , ∀i ∈ {1, 2, . . . , n − 1}, (vi∗ , vi+1
∗
) ∈ E,
that visits each vertex exactly once, we can get the following points:
• Every vertex appears only once: for any i, j ∈ {1, 2, . . . , n} and i ∕= j,
vi∗ , vj∗ ∈ V and vi∗ ∕= vj∗
• And according to the definition of the topological number, we can find
if i < j, then T op(vi∗ ) < T op(vj∗ )
• Now we prove ∀i ∈ {1, 2, . . . , n}, T op(vi∗ ) = i. We complete the proof
from case i = n to case i = 1:
– Case i = n: if T op(vn∗ ) ∕= n, we assume T op(vi∗ ) = n and i <
n. For (vi∗ , vi+1
∗ ∗
) ∈ E, T op(vi+1 ) > T op(vi∗ ) = n holds and a
contradiction occurs.
– Other Case: The similar proof for case i = n − 1, n − 2, . . . , 1.
We check if there is an edge between each consecutive pair of vertices in
the topological order:
for any i (1 ≤ i < n), (T opsort[i], T opsort[i + 1]) is in E or not?
Problem 6
Given a Direct Acyclic Graph G = (V, E), design a linear-time O(m + n)
algorithm to determine whether a digraph has a unique topological ordering.
6
Solution
a) For a DAG G = (V, E), we first compute a topological order T op(vi ) of all
vertice. We prove that a DAG has a unique topological ordering if and only
if for any two vertices vi and vj with consecutive topological number T op(vi ),
T op(vj ) = T op(vi ) + 1, there is a edge from vi to vj . That also means there
is a path visiting every vertices.
• ←) according to the definition of topological number, since there is a
edge from vi to vj , in any other topological ordering T op∗ ( ) we can get:
T op∗ (vi ) < T op∗ (vj )
which means T op( ) and T op∗ ( ) are the same orders.
• →) the proof is similar to ← case.
Problem 7
A directed Eulerian cycle is a directed cycle that contains each edge exactly
once. Given a graph G = (V, E), design a linear time algorithm to find a
directed Eulerian cycle or report that no such cycle exists.
Solution
Prove that a digraph G has a directed Eulerian cycle if and only if vertex in
G has its indegree equal to its outdegree and all vertices with nonzero degree
belong to the same strong component.
Problem 8
An array is bitonic if it is comprised of an increasing sequence of integers
followed immediately by a decreasing sequence of integers. Write a program
that, given a bitonic array of n distinct int values, determines whether a
given integer c is in the array. Your program should use O(lg n) compares in
the worst case.
7
Solution
Use a version of binary search to find the maximum of this array in lg n
compares; then use binary search to search in two pieces separately.
function int Binary-Search-FindMax(Array A, int f irst, int last )
if f irst < last then
int middle = (f irst + last)/2;
if A[middle] > A[middle + 1] then
return Binary-Search-FindMax(A, f irst, middle);
else
return Binary-Search-FindMax(A, middle + 1, last);
end if
else
return f irst
end if
end function
Problem 9
Given a set of comparable elements, the ceiling of x is the smallest element
in the set greater than or equal to x, and the floor is the largest element less
than or equal to x. Suppose you have an array A[ ] of n distinct items in
ascending order. Give an O(lg n) algorithm to find the floor and ceiling of x.
Solution
Use a version of binary search to find two consecutive elements A[i] and
A[i + 1] in the array with A[i] ≤ x ≤ A[i + 1].
function int FindCF(array A, int f irst, int last, array B, float x)
if f irst < last then
int middle = (f irst + last)/2;
if A[middle] = x then
B[0] = middle; Return A[B[0]] as the ceiling of x
B[1] = middle; Return A[B[1]] as the floor of x
end if
if A[middle] > x then
if middle = 0 then
8
B[0] = −1, B[1] = 0; All elements are larger than x.
end if
if A[middle − 1] < x then
B[0] = middle − 1, B[1] = middle;
else
Find-Ceiling-Floor(A, f irst, middle − 1, B);
end if
end if
if A[middle] < x then
if A[middle + 1] > x then
B[0] = middle, B[1] = middle + 1;
else
Find-Ceiling-Floor(A, middle + 1, last, B);
end if
end if
else
if A[f irst] = x then
B[0] = B[1] = f irst;
else
B[0] = f irst, B[1] = −1; All elements are less than x.
end if
end if
end function
Problem 10
Given an array of n elements in which each element is an integer between 1
and n, write an algorithm to determine if there are any duplicates in linear
O(n) time and O(1) extra space .
Solution
function boolean find-Duplicates(int Array A[1 . . . n])
for i = 1; i ≤ n; i + + do
if A[i] ∕= i then
ok = f alse;
end if
9
j = i;
while ok = f alse do
if A[A[j]] = A[j] then
return True;
else
j = A[A[j]];
A[A[j]] = A[j];
if j = i then
ok = true;
A[j] = j;
end if
end if
end while
end for
return False;
end function
Problem 11
Write an algorithm to determine whether a graph G = (V, E) is 2-colorable.
The algorithm should run in O(n + m), where n = |V | and m = |E|, and
produce a 2-coloring if one exists.
Problem 12
Given an n-bit binary integer N , hence N can denoted as an array N [1, . . . , n]
with N [i] ∈ {0, 1}. The two critical operations are: (15)
• Additions: Add(x, y) adds two binary integers x and y, and assigns
the result to x
• Shifts: Shift(x, t) shifts binary digits of x one places to the left (t = 1)
or to the right (t = −1)
You are asked to design two algorithms:
a design an algorithm to compute N 2 using O(n) additions and shifts.
√
b design an algorithm to compute ⌈ N ⌉ using only O(n2 ) additions and
shifts.
10
Problem 13
All elements in a given array A[1 . . . n] are identical except one (distinguished
one). (20)
a Design an algorithm that finds the distinguished element using at most
⌈(n + 1)/2⌉ comparisons.
b Argue that ⌈(n + 1)/2⌉ is the tight bound for this problem.
c Determine the average (expectation) number of comparisons of your
algorithm. Assume that the location of the distinguished element is a
random number within [1 . . . n].
Problem 14
Given a directed graph G with n vertices and m edges,
a design a O(n + m) algorithm to test whether a specific vertex v can
reach all other vertices in the graph;
b design a O(n + m) algorithm to test whether there is a vertex in G
which can reach all other vertices in G.
Problem 15
A directed graph G = 〈V, E〉 is semiconnected if, for all pairs of vertices
u, v ∈ V , we have u → v (i.e., there is a path from u to v in G) or v → u.
Give an efficient algorithm to determine whether or not G is semiconnected.
Prove that your algorithm is correct, and analyze its running time.
Problem 16
Suppose we wish not only to increment a counter but also to reset it to zero
(i.e., make all bits in it 0). Counting the time to examine or modify a bit
as Θ(1), show how to implement a counter as an array of bits so that any
sequence of n INCREMENT and RESET operations takes time O(n) on an
initially zero counter.
11
Problem 17
An ordered stack is a data structure that stores a sequence of items and
supports the following operations.
• OrderedPush(x) removes all items smaller than x from the beginning of
the sequence and then adds x to the beginning of the sequence.
• Pop deletes and returns the first item in the sequence (or Null if the
sequence is empty).
Suppose we implement an ordered stack with a simple linked list, using
the obvious OrderedPush and Pop algorithms. Prove that if we start with
an empty data structure, the amortized cost of each OrderedPush or Pop
operation is O(1).
Problem 18
Recall that a standard (FIFO) queue maintains a sequence of items subject
to the following operations.
• Push(x): Add item x to the end of the sequence.
• Pull(): Remove and return the item at the beginning of the sequence.
• Size(): Return the current number of items in the sequence.
(a) Design an algorithm with proper data structure to implement a queue,
satisfying that it uses O(n) space (where n is the number of items in the
queue) and the worst-case time for each of these operations is O(1).
(b) Consider the following new operation, which removes every tenth
element from the queue, starting at the beginning, in Θ(n) worst-case time.
Prove that in any intermixed sequence of Push, Pull, and Decimate operations,
the amortized cost of each operation is O(1).
Problem 19
Describe how to implement a queue using two stacks and O(1) additional
memory, so that the amortized time for any enqueue or dequeue operation
is O(1). The only access you have to the stacks is through the standard
subroutines Push and Pop.
12
function Decimate( )
n ← Size();
for i ← 0 to n − 1 do
if i mod 10 = 0 then
Pull();
Size() ← Size() − 1;
else
Push(Pull());
end if
end for
end function
Solution
We set the enqueue/dequeue points of the queue as the start/end of the queue.
Briefly, we employ two stacks (Stacks and Stacke ) to sort the whole elements
into two parts, i.e., one part from the start point and the other part from
the end point.
function enqueue(x)
Stacke .Push(x)
end function
function dequeue
if Stacks .Isempty() = True then
while Stacke .Isempty() ∕= True do
k = Stacke .Pop()
Stacks .Push(k)
end while
end if
return Stacks .Pop()
end function
We analyze the following implementation, for any elements x in the queue,
• first, x is pushed in Stacke (i.e., enqueue(x));
• second, x is poped from Stacke and pushed in Stacks ;
• finally, x is poped from Stacks .
On the whole, for x, we implement two Pushs and two Pops. We move all
these four operators into the enqueue operation of x.
13
Problem 20
Relative Strength Index (RSI) is a Commonly Used Technical Analysis Indi-
cator. The Relative Strength Index (RSI) is a widely used technical analysis
indicator for measuring the strength and weakness of financial market prices.
It is based on the following formulas:
Calculate the price change (gain or loss):
Gain=Current price - Previous price
Compute the average gain and loss over a sliding window of size N:
Average Gain (AvgGain)=(Sum of gains over the past N days)/N
Average Loss (AvgLoss)=(Sum of losses over the past N days)/N
Compute the Relative Strength (RS):
RS = AvgGain/AvgLoss
Compute the RSI:
RSI = 100 - 100/(1 + RS)
For the price sequence [45, 46, 47, 46, 45, 44, 45, 46, 47] with a sliding
window size N=5: At the point where the price is 44, the sliding window
contains [45, 46, 47, 46, 45, 44]. Compute the differences between consecutive
prices: [1.0, 1.0, -1.0, -1.0, -1.0].
Average Gain = (1.0 + 1.0) / N,
Average Loss = -(-1.0 - 1.0 - 1.0) / N.
RS = AvgGain/(AvgLoss ) =2/3,
RSI = 100 - 100/(1 + RS) = 40.0.
Implement an algorithm to compute the RSI indicator. Use a Queue to
maintain a sliding window of size N. Dynamically update the window and
calculate the average gain and average loss in real-time as the window slides.
Input: A sequence of prices, e.g., [45, 46, 47, 46, 45, 44, 45, 46, 47] Sliding
window size N, e.g., N=5
Output: The computed RSI values after each window slide, e.g., [nan,
nan, nan, nan, nan, 40.0, 52.0, 61.6, 69.28].
Problem 21
A vertex cover of a graph G = (V, E) is a subset of vertices S ⊆ V that in-
cludes at least one endpoint of every edge in E. Give a linear-time algorithm
for the following task. (15)
Input: An undirected tree G = (T, E) with the root vertex s.
Output: The size of the smallest vertex cover of T .
14
Solution
In the tree T , the total tree could be considered as a root node and several
sub-trees rooted at its children nodes. We design two array selected[1 . . . n]
and ignore[1 . . . n], where selected[v] denotes the size of the smallest vertex
cover of the sub tree rooted at v, when v is added in this vertex cover;
ignored[v] denotes the size when v is not added in this vertex cover. For a
node v, the value of selected[v] and ignored[v] are recursively defined by the
values of its children nodes w:
selected[v] = 1 + min{selected[w], ignore[w]}
ignore[v] = selected[w]
The size of the smallest vertex cover of T rooted at s is
max{selected[s], ignore[s]}
function void main(Intlist[ ] adjV ertices, int n, int root, int size)
int[ ] color=new int[n];
int[ ] selected=new int[n];
int[ ] ignored=new int[n];
int v = root;
Initialize color array to white for all vertices;
vcDFS(adjV ertices, color, v, selected, ignored);
return min{selected[v], ignored[v]};
end function
function void vcDFS(IntList[ ] adjV ertices, int[ ] color, int v, int [ ]
selected, int [ ] ignored)
int w;
IntList remAdj;
color[v] = gray;
remAdj = adjV ertices[v];
selected[v] = 1;
ignored[v] = 0;
while remAdj ∕= nil do
w = f irst(remAdj);
if color[w] == white then
15
vcDFS(adjV ertices, color, w, selected, ignored);
selected[v]+ = min{ignored[w], selected[w]};
ignored[v]+ = selected[w];
remAdj = rest(remAdj);
end if
end while
color[v] = black;
return ;
end function
Problem 22
Professor Li is consulting for the president of a corporation that is planning
a company party. The company has a hierarchical structure; that is, the
supervisor relation forms a tree rooted at the president. In order to make
the party fun for all attendees, the president does not want both an employee
and his or her immediate supervisor to attend.
Professor Li is given the tree containing n nodes, that describes the struc-
ture of the corporation. Each node of the tree holds, in addition to the
pointer, the name of an employee and that employee’s conviviality ranking.
Describe an algorithm to make up a guest list that maximizes the sum of the
conviviality ratings CR() of the guests. Analyze the running time of your
algorithm.
Solution
In the tree T , the total tree could be considered as a root node and several
sub-trees rooted at its children nodes. We design two arrays selected[1 . . . n]
and ignore[1 . . . n], where selected[v] denotes the max sum of conviviality
ratings of the guests selected from the sub tree rooted at v, when v is added
in the guest list; ignored[v] denotes the max sum when v is not added in the
guest list. For a node v, the value of selected[v] and ignored[v] are recursively
defined by the values of its children nodes w:
selected[v] = CR(v) + ignore[w]
ignore[v] = max{selected[w], ignore[w]}
16
And for roof nodes v, selected[v] = CR(v) and ignore[v] = 0; The max sum
of conviviality is
max{selected[s], ignore[s]}
Problem 23
You have collected n distinct values into an array A[1...n]. Your goal is to
find the longest period i . . . j, i.e., maximize j − i, such that A[i] < A[j]. The
values between A[i] and A[j] do not matter. As an example, consider the
following array A[1...7]:
A[1] = 14, A[2] = 6, A[3] = 8, A[4] = 1, A[5] = 12, A[6] = 7, A[7] = 5
Your algorithm should return a span of 4 since A[2] = 6 and A[6] = 7.
The next biggest span is A[4] = 1 to A[7] = 5.
(a) Give an O(n)-time algorithm to compute the minimums of the prefix
A[1...k] for each k, and store in M A[k]: M A[k] = minki=1 A[i].
(b) Using the M A[i] computed above,give an O(n log n)-time algorithm to
maximize j − i subject to A[i] < A[j].
17