Lecture 12

advertisement
ANALYSIS OF ALGORITHMS
Sorts
QuickSort
Quicksort is based on dividing the original array a[1:n] into 2 sub-lists, a[1:p],a[p+1:n] by
rearranging the elements with respect to a pivot element, and where the elements of a[1:p] are less
to the pivot, and the elements of a[p+1:n] are larger than the pivot. By “partitioning” the original
list into these 2 sub-lists, we can recursively apply the same partitioning algorithm to these sublists, and so on, thus we will have rearrange the original list into a “sorted” list.
Quicksort uses the function “Partition”, that partitions a list into two sub-lists rearranged according
to a pivot element.
Example 1:
Consider the following list:
a[1:7]=50, 60, 30, 70, 40, 20,10
We choose a[1]=50 as the pivot element v, thus we are going to rearrange a[ ] (by applying the
partition algorithm) with respect to v=50.
To accomplish this, we will first insert infinity () at the end of the list, thus
a[1:8]=50, 60, 30, 70, 40, 20,10, 
We will use two pointers, l pointing to the first elements a[1], and r pointing to the last element
a[8]= .
Thus
50, 60, 30, 70, 40, 20,10, 
r
l
Next will increase a[l] by 1, and determine if a[l] < v
50, 60, 30, 70, 40, 20,10, 
l
r
Since 60 is not < 50, we do not increase l by one, we then decrease the r pointer and try to
determine if a[r] > 50.
50, 60, 30, 70, 40, 20,10, 
l
r
A this stage clearly we must swap a[l] with a[r], and then continue.
50, 10, 30, 70, 40, 20, 60, 
l
r
Next will keep increasing the l pointer until we find an element that is > than 50.
50, 10, 30, 70, 40, 20, 60, 
l
r
Next we will decrease the r pointer until we find an element that is < than the pivot.
50, 10, 30, 70, 40, 20, 60, 
l
r
We swap a[l] with a[r]
50, 10, 30, 20, 40, 70, 60, 
l
r
Next will keep increasing the l pointer until we find an element that is > than 50.
50, 10, 30, 20, 40, 70, 60, 
r
l
Next we will decrease the r pointer until we find an element that is < than the pivot.
50, 10, 30, 20, 40, 70, 60, 
r
l
At his stage, the pointers crossed each other, thus we stop.
Finally we swap a[1] with a[r], thus
40, 10, 30, 20, 50, 70, 60, 
r
l
Please note that from a[1:r] we have the elements that are less or equal than v=50, and from a[l:8],
we have the elements that are larger than v=50.
Clearly the partition point of the original list is r, that is the two sub-list are
a[1:r], and a[l:8].
The following is the partition algorithm
A16: int Partition(int a[ ], int m, int q)
{ int v=a[m]; pivot is set to the first element.
int l= m, r=q;
do {
do l++;
(1)
while (a[l] < v);
do r--;
(2)
while (a[r] > v);
if (l < r) swap (a, l, r);
}
while (l < r);
a[m]=a[r]; a[r]=v; //swap a[m] with a[r]
return r; // r is the partition point.
}
The Quicksort algorithm partitions a list a[m:p] into 2 sub-lists and then calls recursively these
sub-lists independently:
A17: void Quicksort (a[ ], int m, int p)
{
if ( m < p)
{
int k=Partition(a,m,p+1); //note that the element a[p+1] is included
// in the partition
Quicksort (a, m, k-1);
Quicksort (a, k+1,m);
}
}
Example 2:
Consider the following list:
a[1:7]=50, 60, 30, 70, 40, 20,10
Remember that we insert the  at the end of the list
a[1:8]=50, 60, 30, 70, 40, 20,10, 
First call is Mergesort (a,1,7).
m=1, p=7.
By looking at Example 1 we have k=Partition (a,1,8) =5
And the two sub-lists are
[ 40, 10, 30, 20, 50] , and [ 70, 60, ],
thus we call recursively Quicksort (a,1,4) and Quicksort (a,6,7)
Quicksort (a,1,4)
Quicksort(a,6,7)
m=1, p=4, thus m < p.
m=6, p=7, m < p
We call Partition(a, 1, 5) :
We call Partition (a,6,8):
40, 10, 30, 20, 50
70,
r
l
60,
l

r
The pivot is v=40
The pivot is v=70
The l pointer will go all the way to the end
The l pointer goes to the end
40,
10,
30,
20,
50

70, 60,
r
r
l
l
Next we move the r pointer
Next we move the r pointer
40,
70,
10,
30,
20,
50
r
l
60,
r

l
They crossed each other thus we stop
They crossed each other thus we stop
Next we swap a[1] with a[r]
Next we swap a[1] with a[r]
20,
60, 70, 
and the partition point is k=7
10,
30,
40,
50
r
l
and the partition point is k=4 (the index
and not the element)
We call Quicksort (a,1,3) and
Quicksort (a,5,4) but in the last case
m>p, thus nothing is done.
Next we call Quicksort (a,6,6) thus
m=p thus nothing is done, and
Quicksort (a,8,7) thus m>p thus
again nothing is done.
Quicksort (a,1,3)
m=1, p=3, thus m <= p.
We call Partition(a, 1, 4) :
20,
10,
30,
l
40
r
The pivot is v=20
We move the l pointer.
20,
10
30,
l
40
r
Next we move the r pointer
20,
10,
30,
r
l
40
They crossed each other thus we stop
Next we swap a[1] with a[r]
10,
20,
r
30,
40
l
and the partition point is k=2 (the index
and not the element)
Finally, we call Quicksort (a,1,1), and Quicksort(a,3,3)
But in both cases m=p thus we do not do anything.
Worst-case analysis.
Since Quicksort is based on the Partition Algorithm, if we consider the “comparison” of the basic
operation, instructions (1) and (2) of algorithm A16 are the operations to be considered, that is, to
compare the element a[l] with the pivot v, and to compare a[r] also with v.
Suppose we have a list of n elements to be considered by Partition. We note that first increment l
and then compare a[l] with the pivot until we find an element that is larger than v. Next we start
decrementing r until we find an element a[r] that is less than the pivot. Thus everytime we compare
an element with the pivot, either we just incremented l or decremented r, thus the number of
comparison is n.
Consider a list of n elements in decreasing order. For example
a[ ]= 10, 9, 7, 5, 4, 3, 2, 1, thus n=8.
The first call we make is Quicksort (a,1,8). In this case m=1, and p=8, thus we call Partition (a, 1,
9):
10, 9, 7, 5, 4, 3, 2, 1, 
l
r
Thus the pivot is v=10.
We start incrementing l, but we note that l goes all the way to end.
10, 9, 7, 5, 4, 3, 2, 1, 
r
l
We decrement r just once since the pointers crossed each other.
10, 9, 7, 5, 4, 3, 2, 1, 
r
l
Finally we swap a[1] with a[r],
1, 9, 7, 5, 4, 3, 2, 10, 
r l
Thus the Partition point is k=8, thus we call
Quicksort (a,1,7) and Quicksort(a, 9,8), but Quicksort(a, 9,8) does do anything since 9 > 8.
As we previously mentioned, Partition will make 9 basic operations.
Thus consider Quicksort(a,1,7), thus m=1 and p=7. This calls Partition (a,1,8) thus
1, 9, 7, 5, 4, 3, 2, 10
l
r
thus the pivot is v=1.
We then increment l by one, and we then stop. Next we decrement r all the way to the beginning.
1, 9, 7, 5, 4, 3, 2, 10
r
l
Thus the partition point is k=1, thus we call Quicksort(a,1,0) and Quicksort (a,2,7),
but Quicksort(a,1,0) does not do anything at all.
We also note that since Partition process a list of 8 elements it make 8 basic operations.
Clearly we notice that Quicksort start processing a list of 8 elements (Partition process 9), and We
call Quicksort to process a list of 7 elements (Partition process 8), and everytime we decrease the
list by 1:
Quicksort (a,1,8)
Partition makes 9 comparisons.
Quicksort (a,1,7)
Partition makes 8 comparisons.
Quicksort (a,2,7)
Partition makes 7 comparisons.
Quicksort (a,2,6)
Partition makes 6 comparisons.
Quicksort (a,3,6)
Partition makes 5 comparisons.
Quicksort (a,3,5)
Partition makes 4 comparisons.
Quicksort (a,4,5)
Partition makes 3 comparisons.
Quicksort (a,4,4)
Partition makes 2 comparisons.
Thus in general if we start with a list of n elements in decreasing order we have our worst-case
W(n)=(n+1) + n +(n-1)+ (n-2) + (n-3)+……..+ 2.
(n  1)(n  2)
n 2  3n  2
Thus W (n)   i 
 1
 1   (n 2 )
2
2
i2
n 1
Average-Case
The average case analysis is a little more complicated by we will sketch a simple proof.
We do not know what is the partition point is in a list of n elements, but at the average we can prove
that the partition point is at k=n/2.
Thus after we call Quicksort(a,1,n), we then call Partition(a,1,n+1), where partition performs n+1
operations.
The partition point returned by Partition is k=n/2, thus we call Quicksort(a,1,n/2-1) and Quicksort
(a,n/2+1,n)
Thus the average case is
T(n)=T(n/2) +T(n/2) + n+1=2T(n/2) + n+1 (nlogn).
Download