Chapter 2
2.1-3:
Linear_Search(A, n, v)
for i<-1 to n
if A[i]=v
return i
return NIL
Loop invariant: before the loop i, the 1 to i-1 items in the array do not contain the value "v".
Proof
Initialization: i-1=0, so this condition is trivial.
Maintenance: let's suppose before the loop i=k, the condition meets. That is the items [1... k-1] do not contain value "v". Let's consider the loop while i=k: if A[k]=v, then the loop will terminate by returning the index k. If A[k]!=v, then after this loop, the items [1...k] all do not contain value "v".
Termination: When the loop ends, if i=n+1, the return value is NILL. Since the items [1...n] all do not contain value "v", the result is expected; if i<n+1, it means that A[i]=v exists the loop. We have found the item with value "v" and returned the index we want.
2.2-1: n^3
2.2-2:
for j<- 1 to n-1
min<- j
for i<- j+1 to n
if A[i]<A[min]
min<-i
temp<- A[j]
A[j]<- A[min]
A[min]<- temp
Loop invariant: in A[], the elements with index less than and equal to j-1 are in increasing sorted order and they are the j-1 least elements in the A[].
Proof
Initialization: j=1 so j-1=0 this is trivial.
Maintenance: Suppose we have this conclusion after j=k loop. Let prove it while j=k+1. While j=k+1, the outer for loop will find the minimum element between k+1 and n. After exchanging this minimum element with A[k+1], the A[k+1] is the smallest element in A[k+1, ... n].
Since A[1...k] is in increasing sorted order and they are less than A[k+1...n], after this for loop, the current A[k+1] is also lager than A[1...k]. So A[1... k+1] are in increasing sorted order. Also, A[k+1] is less than A[k+2...n], so are elements A[1...k]. So A[1...k+1] are the K+1 least elements in A[].
Termination: When the outer for loop ends, j=n. So the A[1...n-1] are in increasing sorted order. Also A[1...n-1] are less than A[n]. So the A[1...n] are all in increasing sorted order. This is what we want to achieve.
Based on the termination above, it is easy to understand why the outer loop can end with n-1.
Let tj=the time spent on min<-i for each j loop, then
T(n) = c1*n + c2*(n-1) + sum(j=1...n-1)( n-j + n-j + tj) + (c3+c4+c5)(n-1)
= bn+c+ n^2 -n + sum(j=1...n-1)(tj)
Best case: tj=0. This occurs while A[] is in increasing sorted order. T(n)=n^2 +bn+c
Worst case: tj=n-j. This occurs while A[] is in decreasing sorted order. T(n)=3/2*n^2+bn+c
2.2-3: n/2; n; theta(n/2)=theta(n).
2.2-4: We may simply add a testing for the increasing sorted order in font of any algorithm so that if we find it is the best case, we will have a good running time. This practise tells us that the best case running time of an algorithm makes no sense.
2.3-2:
Merge(A, p, q, r)
n1<- q-p+1
n2<- r-q
create arrays L[1..n1], R[1...n2]
for i<-1 to n1
L[i]<-A[p+i-1]
for j<-1 to n2
R[i]<-A[q+i]
i<-1
j<-1
k<-p
while i<n1+1 and j<n2+1
if L[i]<R[j]
A[k]<-L[i]
i<-i+1
else
A[k]<-R[j]
j<-j+1
if i<n1+1
while k<r
do A[k]<-L[i]
k<-k+1
i<-i+1
if j<n2+1
while k<r
do A[k]<-R[j]
k<-k+1
j<-j+1
2.3-3:
1. when n=2, T(n)=2lg2=2. The conclusion is correct.
2. let's suppose when n=2^k, T(n)=nlgn. =>T(2^k)=2^klg(2^k)
When n=2^(k+1),
T(n)=2T(n/2)+n
=2T(2^k)+2^(k+1)
=2*2^k*lg(2^k)+2^(k+1)
=2^(k+1)*k+ 2^(k+1)
=(k+1)*2^(k+1)
=2^(k+1)*lg(2^(k+1))
=n*lgn
2.3-4:
Insertion_Sort(A, n)
if n<2
return
Insertion_Sort(A, n-1)
key<-A[n]
i<-n-1
while i>0 and A[i]>key
A[i+1]<-A[i]
i<-i-1
A[i+1]<-key
Translate into C# code:
Let's suppose t(n) to be the running time of the while loop for given variable n, then:
T(n)= C + T(n-1) + t(n)
In the worst case, the while loop will always terminate when i<=0. In this scenario, the t(n)= theta(n), so:
theta(1) n=1
T(n)= {
T(n-1) + theta(n) n>1
2.3-5:
Binary_Search(A, p, r, v)
if p>r
return NIL
q<- [p+r]/2
if A[q]=v
return q
if A[q]>v
return Binary_Search(A, p, q-1, v)
else
return Binary_Search(A, q+1, r, v)
The worst case occurs when the recurrence stops when p>r and returns NIL. Let's suppose n is the power of 2 and n=2^k.
T(n)=C+T(n/2)
=2C+T(n/4)
=3C+T(n/8)
....
=kC+T(n/2^k)
=kC+T(1)
=(k+1)C
n=2^k => log2(n)=k
So T(n)=(log2(n)+1)C=theta(lg(n))
Chapter 4
4.1-1
Use mathematics induction. (It seems that there is no basic value condition to prove)
Let's suppose T([n/2]+1)<=clg(n/2+1)=c*lg(n+2) -c
We wanted to prove c*lg(n+2) -c +1 <=clgn
=>c*lg(n/(n+2))>=1-c
=>c*lg(2n/(n+2))>= 1
=> lg(2 - 4/(n+2))>= 1/c
Since f(n) = lg(2 - 4/(n+2)) function increases with the n, f(3) = lg(4/3).
So for any c>=1 we have f(n)>=1/c for all n>=3. That is the induction of T(n)<=n hold true for all n>=3.
So T(n)=O(lgn).
4.1-2.
Use mathematics induction.
Let's suppose T(n/2)>= clgn/2=clgn - c
T(n)=2T(n/2)+n>=2clgn-2c+ n
So for any given const c, we have T(n)>= clgn for all n>[2c]
So T(n)=omega(nlgn). So T(n)=theta(nlgn)
4.1-3
Inductive hypothesis: T(n)=O(nlg(n+1))
T(1)=1<=c*1*lg2=c for any C>=1
Let's suppose T(n/2)<= cn/2*lg(n/2+1)
T(n)<=2*cn/2*lg(n/2+1)+n=cn*lg(n/2+1)+n
To get the conclusion of cn*lg(n/2+1)+n<=cnlg(n+1) <= 1<c*lg((n+1)/(n/2+1)) <=1/c<lg((2n+2)/n+2) <=1/c<lg(2-2/(n+2))
2-2/(n+2) will increase with the increasing variable "n". So it will get the minimum value at n=1, which lg(2-2/(n+2))=lg4/3. So let c>=[1/lg4/3], this
Chapter 5
Exercises 5.1-2:
RANDOM(a, b)
value<-a
for i<-a to b-1
value <- value + RANDOM(0, 1)
return value
The expected running time is Θ(b-a)
Exercises 5.1-3:
Analysis: We only have the procedure BIASED-RANDOM, which output 0 or 1 with 1-p and p probabilities. So we will run it k times to get the unbiased result. By using probability theory, this is a n-times bernoulli trial, the probability of getting x 0 and n-x 1 is C(n, x)(1-p)^x* p^(n-x). Since we do not know the value of p, it is impossible to make C(n, x)(1-p)^x* p^(n-x) to be 1/2.
Let's try some small times, n=2.
(0, 0) (1-p)^2
(0, 1) (1-p)*p
(1, 0) p*(1-p)
(1, 1) p^2
As we can see, (0, 1) and (1, 0) will appear with equal probability. Also, since these 4 events are independent, we may only check (0, 1) and (1, 0). They should appear with equal probability. We may output value 0 for (0, 1) and value 1 for (1, 0).
UNBIASED-RANDOM()
while true
x<- BIASED-RANDOM()
y<- BIASED-RANDOM()
if x != y
return x
Let's define the random variable Y: the times of trial the BIASED-RANDOM() will first output (0, 1) or (1, 0). This is a geometric distribution.
Let's define x=2*p(1-p) => P{Y=k}= (1-x)^(k-1) * x.
So the expection of Y is:
E[Y]= sigma(k=1 to n)(k*(1-x)^(k-1) * x)
= 1/x
= 1/(2*p(1-p))
So the expected running time is Θ(1/(2*p(1-p)))
Exercises 5.2-1:
Hire exact one time means the best candidate comes first position, let's define this event as Y1. Since the best candidate can appear in all n position with equal probability, Y1=Y2=....=Yn. Also, P(Y1)+P(Y2)+....P(Yn)=1. So P(Y1)=1/n.
Hire n times means the candidate are placed in increasing order. There is only one element for this order. Since the sample space elements number is n!. So P(A)= 1/n!.
Exercises 5.2-2:
Since the first item will always be hired and the candidate with rank "n" will also be hired, we must keep that rank "n" candidate is not in position 1. Let's define the candidate in position 1 with rank "i", so all the rank above "i" candidates should reside behind the rank "n" candidate.
Let's define the position of rank "n" candidate to be "j". The event that the candidate position has value "i" is Ei. So Pr(Ei) = 1/n.(All n candidates have the equal probability at position 1). When rank "i" is at position 1, let's define event F to be rank i+1, i+2... n candidates are behind candiate rank "n". So what we wanted to get is: sigma(i=1 to n-1)(Pr(Ei)Pr(Ei|F)). The key point is to figure out Pr(Ei|F).
When rank "i" is at position 1, all other n-1 elements have permutation, this is the sample space. In this sample space, we care the candidates with rank>i: i+1, i+2....n. The good elements occur when the rank "n" candidate is the first one in the permutation. Since these n-i candidates all have the same probability to appear first in permutation, so the rank "n" candidate appears first has the probability 1/(n-i). That is Pr(Ei|F)=1/(n-i).
sigma(i=1 to n-1)(Pr(Ei)Pr(Ei|F)) = 1/n * sigma(i = 1 to n-1)(1/i) = 1/n* H(n-1). H(n) is the harmonic number.
Exercises 5.3-2:
No. Let's compute how many permutations do we get in this algorithm. For element 1, we have 2, 3, ...n for choice, so we have (n-1) possible choices. For element 2, we have (n-2) possible choices. Generally, for element i, we have (n-i) choice. After all the algorithm, we have (n-1)(n-2)(n-3)...1 = (n-1)! permutations.
Since Professor Kelp wanted to get n!-1 permutations, while n!-1 != (n-1)!, the algorithm did not provide the correct result to Professor Kelp.
Exercises 5.3-3:
No. Le'ts also compute how many permutations do we get in the algorithm.
For any element i, we have n choice. After all the algorithm, we have n^n permutations. Although n^n> n!, this does not mean the permutation is not uniform random permutation. Let's assume this is uniform random permutation, then all the n! permutation should have the same number of times in the n^n results.
=> there must be an integer m: 1/n! = m/n^n => m=n^n/n! for any givin n will be true. But, this is not always true for all integer n.
For example, let's consider n=3, m=9/6=3/2 is not an integer. So our assumption is not true, the algorithm will not always compute uniform random permutation.(Note: for n=2, it will, interesting!)
Chapter 6
6.1-1: min--2^h and max--2^(h+1) -1
6.1-2: Let define the x to be the height of the heap. With 6.1-1, we have 2^x<=n<=2^(x+1)-1
=> x<=log2(n)<(x+1)
=> x<=log2(n) and x>log2(n)-1
=> x=[log2(n)]=[lgn]
6.1-4: Leaf node. Any non-leaf node will be larger than the leaf node, because all the nodes are distinct.
6.2, Q: How to prove that, for a n-nodes heap, the maximize sub-heap size is less than 2*n/3?
A: Let define the sub-heap size to be k, so k<n. To make k/n maximized, we should get the largest k for the sub-heap. This is because (k+i)/(n+i) is larger than k/n. Ok, we should take the sub-heap a full binary tree. Then, we should get the minimum value n, so the right sub-heap should be empty in the last level. In all, we may get a half full heap. Let's define the heap height as h. So k=1+2^1+2^2+...2^(k-1) = 2^k -1, while n= 1+2^1+2^2+...2^(k-1) + 2^(k-1) = 3*2^(k-1) -1. Since (2/3)*n= 2^k - 2/3 > 2^k -1. So k<2*n/3.
6.3, Let's caculate the running time of BUILD-MAX-HEAP(A) by using the depth d instead of height h.
Since the height of the tree is lgn, the execution time of a node in depth d is: O(lgn - d). The maximize items in depth d is 2^d. So the execution time is sigma(0=<d<=lgn)(2^d*O(lgn-d)). Let's call it S(n), so
S(n) = sigma(0=<d<=lgn)(O(2^d*lgn) - O(2^d * d))
sigma(0=<d<=lgn)(O(2^d*lgn)) = lgn* (2^(lgn + 1) - 1) = O((2n -1)*lgn)
sigma(0=<d<=lgn)(O(2^d*d)) = (lgn - 1)* 2^(lgn+1) +2 = O(2n*(lgn - 1) +2)
=> S(n) = O((2n -1)*lgn - 2n*(lgn - 1) +2) = O(2n - lgn +2) = O(n)
I think this is easier to understand than the caculation in the book.
Exercises 6.2-2:
MIN-HEAPIFY(A, i)
smallest<- i
if 2*i<=heap-size[A] and A[2*i] < A[smallest]
smallest = 2*i
if (2*i+1)<=heap-size[A] and A[2*i + 1]< A[smallest]
smallest = 2*i +1
if smallest != i
temp = A[i]
A[i] = A[smallest]
A[smallest] = temp
MIN-HEAPIFY(A, smallest)
The running time should be the same as MAX-HEAPIFY
Exercises 6.2-3: When the A[i] is larger than its children, the largest will remain as index i, so the recurrence will not occur. The MAX-HEAPIFY will return in const time.
Exercises 6.2-4: When the i> heap-size[A]/2, it is a leaf node. Since it does not have children, the largest will remain as i, so the recurrence will not occur. The MAX-HEAPIFY will return in const time.
Exercises 6.2-5:
MAX-HEAPIFY-LOOP(A, i)
largest <- i
do
if 2*i<= heap-size[A] and A[2*i] >A[largest]
largest<- 2*i
if 2*i+1<= heap-size[A] and A[2*i+1] >A[largest]
largest<- 2*i+1
if largest != i
exchange(A[i], A[largest])
i = largest
else
break
while true
Exercises 6.3-2: Because the calling of MAX-HEAPIFY(A, i) must ensure the 2 sub-trees of node-i are max heaps. If we start from i=1, the heap at node-i may not be a max-heap. If we start from ⌊length[A]/2⌋ to 1, we may ensure before the calling to the MAX-HEAPIFY(A, i), heap at node-i is max heap by using loop invariant.
Exercises 6.3-3: The heap height is [lgn]. So the height h has at most 2^([lgn] - h ) = (2^[lgn])/2^h = ...
Exercises 6.4-2:
Initialization: when i = length[A]. The conclusion is true trivially.
Maintenance: let's assume when i =k, the elements from 1 to k are the smallest k elements in the initial array and the other elements in A[k+1.. n] are sorted. After this loop step for i=k, the heap root node is placed at index k. Since this is a max heap, the root node is the max element in these k nodes max-heap. So after the loop step, the nodes from 1 to k-1 are less than node k. Since these k-1 nodes are also less than the other n-k nodes in assumption, these k-1 nodes are the smallest k-1 nodes in the array. Additionally, based on the assumption, we also know the exchanged node k is less than the other n-k nodes and the n-k nodes are in increasing sorted mode, so these new n-k+1 elements are in increasing sorted mode.
Termination: after the loop, there are only 1 final element in the heap, while the sub-array A[2...n] is sorted increasing. Also, the final node is less than any elements in A[2...n], so all n elements are in sorted increasing.