recursive quicksort data structures in java with junit ©rick mercer
TRANSCRIPT
Recursive QuicksortRecursive Quicksort
Data Structures in Java with JUnitData Structures in Java with JUnit©©Rick MercerRick Mercer
Quicksort: Quicksort: O(n log n) SortingO(n log n) Sorting
Quicksort was discovered by Tony Hoare 1962Quicksort was discovered by Tony Hoare 1962Here is an outline of his famous algorithm:Here is an outline of his famous algorithm:
Pick one item from the array--call it the pivotPick one item from the array--call it the pivot Partition the items in the array around the pivot so all Partition the items in the array around the pivot so all
elements to the left are elements to the left are to the pivot and all elements to the pivot and all elements to the right are greater than the pivotto the right are greater than the pivot
Use recursion to sort the two partitions Use recursion to sort the two partitions a snapshota snapshotpivot
partition: items > pivotpartition 1: items pivot
Before and AfterBefore and After
Let's sort integersLet's sort integers Pick the leftmost one (27) as the pivot valuePick the leftmost one (27) as the pivot value The array before call to partition(a, 0, n-1)The array before call to partition(a, 0, n-1)
Array looks like this after Array looks like this after first first partition is donepartition is done
27 14 9 22 8 41 56 31 14 53 99 11 2 24
24 14 9 22 8 14 11 2 27 53 99 56 31 41
pivotitems < pivot items > pivot
The partition methodThe partition method
ppartitionartition divvies up divvies up aa around the split and around the split and returnsreturns the position of the split, an integer in the the position of the split, an integer in the range of 0..n-1 range of 0..n-1 The postcondition of partition:The postcondition of partition:
a[first]..a[split-1] <= a[split] a[first]..a[split-1] <= a[split] && &&
a[split+1]..a[last] > a[split]a[split+1]..a[last] > a[split]
Notes:Notes: May be more than 1 element equal to the pivotMay be more than 1 element equal to the pivot Put them in left partition Put them in left partition could have been the rightcould have been the right
Recursive call to sort smaller Recursive call to sort smaller part of the arraypart of the array
quickSort(a, split+1, last);quickSort(a, split+1, last); // sort right// sort right
QuickSort the right. At some pointQuickSort the right. At some point Pivot will be 53Pivot will be 53 Assume left portion is already sortedAssume left portion is already sorted
24 14 9 22 8 14 11 2 27 53 99 56 31 41
2 8 9 11 14 14 22 24 27 31 41 53 99 56
pivot
items < pivot items > pivot
left is already sorted, begin to sort part to the right of split
Complete the sortComplete the sort
// sort left and right around new split// sort left and right around new split quickSort(a, first, split-1);quickSort(a, first, split-1);
// sort right// sort right quickSort(a, split+1, last); quickSort(a, split+1, last);
2 8 9 11 14 14 22 24 27 31 41 53 99 56
2 8 9 11 14 14 22 24 27 31 41 53 99 56
2 8 9 11 14 14 22 24 27 31 41 53 99 56
2 8 9 11 14 14 22 24 27 31 41 53 56 99
Entire array is now sorted
Start Over (i ==1)Start Over (i ==1)
Now let's back up and start with empty partitionsNow let's back up and start with empty partitions
int partition(int a[], int first, int last) {int partition(int a[], int first, int last) { int lastSmall = first;int lastSmall = first; int i = (first + 1);int i = (first + 1); // Beginning of unknowns// Beginning of unknowns
Compare all items from a[i]..a[last]Compare all items from a[i]..a[last] if a[i] > a[first] (the pivot), do nothingif a[i] > a[first] (the pivot), do nothing if a[i] <= a[first], swap a[lastSmall+1] with a[i]if a[i] <= a[first], swap a[lastSmall+1] with a[i]
27 41 14 56 31 9 22 8 14 53 99 11 2 24
first lastSmall i unknown items (all are unknown--except first)
Result of the 1st loop Result of the 1st loop iteration(i==2)iteration(i==2)
27 41 14 56 31 9 22 8 14 53 99 11 2 24
first lastSmall i unknown items
partition 1: all items <= pivot partition 2: all items > pivot
The following array shows no changes were made in the array since a[i] <= a[first] was false
So simply add 1 to i (i++)--create 2 partitions Now partition 1 has one element (the pivot 27)
and partition 2 has 1 element (41)
Result of the 2nd loop Result of the 2nd loop iteration(i==3)iteration(i==3)
27 14 41 56 31 9 22 8 14 53 99 11 2 24
first lastSmall i unknown items
partition 1: all items <= pivot partition 2: all items > pivot
The following array shows a swap was made in the array since a[i] <= a[first] was true (14 < 27)
a[i] (14) is swapped with a[lastSmall+1] (41) lastSmall gets incremented to point to the last
element in partition 1 i gets incremented to reference 56
Result of the 3rd loop Result of the 3rd loop iteration(i==4)iteration(i==4)
27 14 41 56 31 9 22 8 14 53 99 11 2 24
first lastSmall i unknown items
partition 1: all items <= pivot partition 2: all items > pivot
The following array shows no swap was made in the array since a[i] <= a[first] was false
lastSmall does NOT get incremented i gets incremented to reference 31
Result of the 4th loop Result of the 4th loop iteration(i==5)iteration(i==5)
27 14 41 56 31 9 22 8 14 53 99 11 2 24
first lastSmall i unknown items
partition 1: all items <= pivot partition 2: all items > pivot
The following array shows no swap was made in the array since a[i] <= a[first] was false
lastSmall does NOT get incremented i gets incremented to reference 9
Result of the 5th loop Result of the 5th loop iteration(i==6)iteration(i==6)
27 14 9 56 31 41 22 8 14 53 99 11 2 24
first lastSmall i unknown items
partition 1: all items <= pivot partition 2: all items > pivot
The following array shows a swap was made in The following array shows a swap was made in the array since a[i] <= a[first] was true (9 < 27)the array since a[i] <= a[first] was true (9 < 27)
a[i] (9) is swapped with a[lastSmall+1] (41)a[i] (9) is swapped with a[lastSmall+1] (41) lastSmall gets incremented to point to the last lastSmall gets incremented to point to the last
element in partition 1element in partition 1 i++ points to the first unknown (22)i++ points to the first unknown (22)
27 14 9 22 56 31 41 8 14 53 99 11 2 24
first lastSmall i unknown items
partition 1: all items <= pivot partition 2: all items > pivot
i == 7i == 7
27 14 9 22 8 56 31 41 14 53 99 11 2 24
first lastSmall i unknown
partition 1: all items <= pivot partition 2: all items > pivot
i == 8i == 8
27 14 9 22 8 14 56 31 41 53 99 11 2 24
first lastSmall i unknown
partition 1: all items <= pivot partition 2: all items > pivot
i == 9 i == 9
27 14 9 22 8 14 56 31 41 53 99 11 2 24
first lastSmall i unknown
partition 1: all items <= pivot partition 2: all items > pivot
i == 10 i == 10
27 14 9 22 8 14 56 31 41 53 99 11 2 24
first lastSmall i unknown
partition 1: all items <= pivot partition 2: all items > pivot
i == 11 i == 11
27 14 9 22 8 14 11 56 31 41 53 99 2 24
first lastSmall i unknown
partition 1: all items <= pivot partition 2: all items > pivot
i == 12 i == 12
27 14 9 22 8 14 11 2 56 31 41 53 99 24
first lastSmall i unknown
partition 1: all items <= pivot partition 2: all items > pivot
i == 13 i == 13
Result of the 14th loop Result of the 14th loop iteration(i==14)iteration(i==14)
27 14 9 22 8 14 11 2 24 53 99 56 31 41
first lastSmall i
partition 1: all items <= pivot partition 2: all items > pivot
The following array shows what happens after traversing the entire array with this loop (i>last):for (i = first + 1; i <= last; i++) {for (i = first + 1; i <= last; i++) { if(a[i] <= a[first] ) {if(a[i] <= a[first] ) { lastSmall++;lastSmall++; swapElements(a, lastSmall, i);swapElements(a, lastSmall, i); } }
}}
Post Loop DetailPost Loop Detail
Now place the pivot into where we expect the Now place the pivot into where we expect the pivot to be: in-between the two partitionspivot to be: in-between the two partitions
swapElements( a, first, lastSmall );swapElements( a, first, lastSmall );
Then we can return lastSmall for the next callThen we can return lastSmall for the next call return lastSmall;return lastSmall;
24 14 9 22 8 14 11 2 27 53 99 56 31 41
first lastSmall (pivot position)
partition 1: all items <= pivot partition 2: all items > pivot
quickSort is called like this:quickSort is called like this:quickSort(a, 0, n-1)quickSort(a, 0, n-1)
void quickSort(int a[], int first, int last) {void quickSort(int a[], int first, int last) { // precondition: a is an array to be sorted from// precondition: a is an array to be sorted from // a[first]..a[last]// a[first]..a[last] if(first >= last) if(first >= last) return; return; // Done: we have an empty array// Done: we have an empty array
// The difficult algorithm is in partition// The difficult algorithm is in partition int split = partition ( a, first, last );int split = partition ( a, first, last );
// Recursively Quicksort left, then right// Recursively Quicksort left, then right quickSort(a, first, split-1); quickSort(a, first, split-1); // sort left// sort left quickSort(a, split+1, last); quickSort(a, split+1, last); // sort right // sort right
// post: the array a is sorted// post: the array a is sorted }}
Analyzing QuicksortAnalyzing Quicksort
The critical statement happens in the comparison The critical statement happens in the comparison of the for loop of the partition functionof the for loop of the partition function
if(a[i] <= a[first]) if(a[i] <= a[first]) So how many times is partition called?So how many times is partition called? And what are the values for first and last (# And what are the values for first and last (#
comparisons)?comparisons)?
If the pivot element is near the mode, we don't If the pivot element is near the mode, we don't have many calls to QuickSort, it is O(log n)have many calls to QuickSort, it is O(log n)
The best of Quicksort, The best of Quicksort, the worst of Quicksortthe worst of Quicksort
In the best case (1st element is always middle In the best case (1st element is always middle value) with 7 elements, call partition 3 timesvalue) with 7 elements, call partition 3 times
first == 0, last == 6first == 0, last == 6 // 6 comparisons// 6 comparisons first == 0, last == 2first == 0, last == 2 // 2 comparisons// 2 comparisons first == 4, last == 6first == 4, last == 6 // 2 comparisons// 2 comparisons
In the worst case, (sorted array), with 7 In the worst case, (sorted array), with 7 elements, partition is called elements, partition is called
first == 0, last == 6first == 0, last == 6 // 6 comparisons// 6 comparisons first == 1, last == 6first == 1, last == 6 // 5 comparisons// 5 comparisons first == 2, last == 6first == 2, last == 6 // 4 comparisons// 4 comparisons first == 3, last == 6first == 3, last == 6 // 3 comparisons// 3 comparisons first == 4, last == 6first == 4, last == 6 // 2 comparisons// 2 comparisons first == 5, last == 6first == 5, last == 6 // 1 comparison// 1 comparison
Best Case:Best Case:
[ 4, 1, 3, 2, 6, 5, 7 ][ 4, 1, 3, 2, 6, 5, 7 ]
[ 2, 1, 3, [ 2, 1, 3, 44, 6, 5, 7 ], 6, 5, 7 ]
[ 2, 1, 3] [ 6, 5, 7 ][ 2, 1, 3] [ 6, 5, 7 ]
[ 1, [ 1, 22, 3] [ 5, , 3] [ 5, 66, 7 ], 7 ]
[1] [3] [5] [7][1] [3] [5] [7]
Worst CaseWorst Case
[ 1, 2, 3, 4, 5, 6, 7][ 1, 2, 3, 4, 5, 6, 7]
[] [2, 3, 4, 5, 6, 7][] [2, 3, 4, 5, 6, 7]
[] [3, 4, 5, 6, 7][] [3, 4, 5, 6, 7]
[] [4, 5, 6, 7][] [4, 5, 6, 7]
[] [5, 6, 7][] [5, 6, 7]
[] [6, 7][] [6, 7]
[] [7][] [7]
[] [][] []
The Best and Worst The Best and Worst continuedcontinued
So in the worst case, partition is called n-1 So in the worst case, partition is called n-1 times:times:
(n-1)+(n-2)+ ... + 1 comparisons = O(n(n-1)+(n-2)+ ... + 1 comparisons = O(n22))
The worst case of Quicksort may be the same The worst case of Quicksort may be the same as Selection Sort, which is O(nas Selection Sort, which is O(n22))
Quicksort is used because of its best and Quicksort is used because of its best and average casesaverage cases