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 i2 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).