Quicksort
Quicksort
Quicksort, like merge sort, uses the divide-and-conquer approach. Merge sort, makes the recursive calls first, then puts the results together to complete the algorithm (as does insertion sort). Quicksort, on the other hand, starts by partitioning the array into two parts in such a way that when recursive calls are made to the parts, the result is a sorted version of the entire array.
Basic idea (ideal version):
Find the median element in the array. Then rearrange the elements in the array so that the elements less than the median come before it in the array, and the elements larger than the median come after it in the array. Then apply quicksort to the part of the array before the median and to the part of the array after the median. The entire array is now sorted.
But there is a problem: How to find the median element?
In fact, if we did, it would take so long that the overall operation of the algorithm would be too slow.
So instead, we do this:
Basic idea (actual version):
Pick an element at random, hoping that it is close to the median. Call this the pivot element. Then rearrange the elements in the array so that the elements less than the pivot come before it in the array, and the elements larger than the pivot come after it in the array. Then apply quicksort to the part of the array before the pivot and to the part of the array after the pivot. The entire array is now sorted.
The partitioning step is done as follows:
int partition(Comparable[] array, int start, int finish){
Comparable pivot = array[start];
int ileft = start;
int iright = finish+1;
while(ileft<iright){
do { ileft++; } while(ileft<=finish && array[ileft].compareTo(pivot)<0);
do { iright--; } while(array[iright].compareTo(pivot)>0);
if(ileft<iright)
interchange(array,ileft,iright);
}
interchange(array,start,iright);
return iright;
}
Then quickSort itself can be written using a helper method which uses starting and finishing array indices as extra parameters:
void quickSort(Comparable[] array, int start, int finish){
if(finish>start){
int pivot = partition(array, start, finish);
quickSort(array, start, pivot-1);
quickSort(array, pivot+1, finish);
}
}
void quickSort(Comparable[] array){
quickSort(array, 0, array.length-1);
}
Analysis
In the worst case, the pivot element is either the largest or smallest element in the array. If that happens every time, quicksort is no better than an insertion sort, with a running time of O(n).In the best case, the pivot element is the median element, as we had originally hoped. In that case, we get the recurrence relation
T(n) = c*n + 2*T(n/2)
This is the same recurrence that we solved for merge sort. The result is that in the best case, quicksort is O(n log n).
It can be shown that in the average case, the pivot element is not too far from the median. Quicksort is O(n log n) on average.
In fact, on average, quicksort performs better than either merge sort or heapsort.