Divide and Conquer: repeatedly cut problem into ``equal'' halves.
``Partition'' the list into two lists:
Partition by selecting a ``pivot'' element value.
If the pivot is about the ``median'' then the lists will be same size.
Scan inwards from left using i. Stop if a[i] >= pivot.
Scan inwards from right using j. Stop if a[j] <= pivot.
If i and j have not crossed each other, swap and continue scan.
The scan is O(n).
Repeat the process on the sublists. Get empty lists in O(log n).
Quicksort: O( )
Quicksort: Example [150]
Pick the pivot to be the first element in the list: 6
Pick pivot:
| 6 | 1 | 4 | 9 | 0 | 3 | 5 | 2 | 7 | 8 |
Hide pivot:
| 8 | 1 | 4 | 9 | 0 | 3 | 5 | 2 | 7 | 6 |
Scan:
| 8 | 1 | 4 | 9 | 0 | 3 | 5 | 2 | 7 | 6 |
| i | j |
Swap:
| 2 | 1 | 4 | 9 | 0 | 3 | 5 | 8 | 7 | 6 |
| i | j |
Scan:
| 2 | 1 | 4 | 9 | 0 | 3 | 5 | 8 | 7 | 6 |
| i | j |
Swap:
| 2 | 1 | 4 | 5 | 0 | 3 | 9 | 8 | 7 | 6 |
| i | j |
Scan:
| 2 | 1 | 4 | 5 | 0 | 3 | 9 | 8 | 7 | 6 |
| j | i |
Swap pivot:
| 2 | 1 | 4 | 5 | 0 | 3 | 6 | 8 | 7 | 9 |
| j | i |
Repeat:
| 2 | 1 | 4 | 5 | 0 | 3 |
| 8 | 7 | 9 |
Quicksort: Median-of-3 Partitioning [151]
What if list is presorted ascending or descending?
What about a sentinel?
New approach: compare the first, center, last element. Pick median.
Also: sort the 3 elements.
input_type median3( input_type a[], int left, int right )
{
int center;
center = (left + right) / 2;
if (a[left] > a[center]) swap( &a[left], &a[center] );
if (a[left] > a[right]) swap( &a[left], &a[right] );
if (a[center] > a[right]) swap( &a[center], &a[right] );
/* invariant: a[left] <= a[center] <= a[right] */
swap( &a[center], &a[right-1] ); /* why? */
return( a[right-1] );
}
Quicksort: quick [152]
% gcc -o quick quick.c > tcc quick.c
Data File with Test Case: quick.dat: 10 6 1 4 9 0 3 5 2 7 8
% quick quick.dat 10 55 40 [ 0 1 2 3 4 5 6 7 8 9 ] 0 0
Quicksort: quick.dat [153]
% quick quick.dat step n move comp 1 2 3 4 5 6 7 8 9 10 i j 10 0 0 [ 6 1 4 9 0 3 5 2 7 8 ] 0 0 10 6 3 [ 0 1 4 9 7 3 5 2 6 8 ] 1 10 10 9 7 [ 0 1 4 2 7 3 5 9 6 8 ] 1 10 10 12 9 [ 0 1 4 2 5 3 7 9 6 8 ] 1 10 10 15 12 [ 0 1 4 2 5 3 6 9 7 8 ] 1 10 10 21 15 [ 0 1 5 2 3 4 6 9 7 8 ] 1 6 10 24 18 [ 0 1 2 5 3 4 6 9 7 8 ] 1 6 10 27 20 [ 0 1 2 3 5 4 6 9 7 8 ] 1 6 10 30 23 [ 0 1 2 3 5 4 6 9 7 8 ] 1 3 10 33 25 [ 0 1 2 3 5 4 6 9 7 8 ] 1 3 10 42 28 [ 0 1 2 3 5 4 6 7 8 9 ] 8 10 10 45 30 [ 0 1 2 3 5 4 6 7 8 9 ] 8 10 10 46 31 [ 0 1 2 3 5 4 6 7 8 9 ] 2 2 10 47 32 [ 0 1 2 3 5 4 6 7 8 9 ] 3 3 10 48 33 [ 0 1 2 3 5 4 6 7 8 9 ] 4 4 10 49 34 [ 0 1 2 3 5 4 6 7 8 9 ] 5 5 10 50 35 [ 0 1 2 3 5 5 6 7 8 9 ] 6 6 10 51 36 [ 0 1 2 3 4 5 6 7 8 9 ] 6 5 10 52 37 [ 0 1 2 3 4 5 6 7 8 9 ] 7 7 10 53 38 [ 0 1 2 3 4 5 6 7 8 9 ] 8 8 10 54 39 [ 0 1 2 3 4 5 6 7 8 9 ] 9 9 10 55 40 [ 0 1 2 3 4 5 6 7 8 9 ] 10 10
Quicksort: quick.c [154]
void q_sort( input_type a[], int left, int right ) {
int i, j;
input_type pivot;
if (left + CUTOFF <= right) { /* CUTOFF should be 20 but example is 2 */
pivot = median3( a, left, right );
i = left; j = right-1; /* why? */
for (;;) {
while (a[++i] < pivot);
while (a[--j] > pivot);
if (i < j)
swap( &a[i], &a[j] );
else
break;
}
swap( &a[i], &a[right-1] ); /* why? */
q_sort( a, left, i-1 );
q_sort( a, i+1, right);
}
}
void quick_sort( input_type a[], int n ) {
q_sort( a, 1, n);
insertion_sort( a, n ); /* n<=20: insertion is better, use CUTOFF */
}
Quicksort: Analysis [155]
Let i= length of the first sublist.
T(n)= running time on a list of n elements.
T(1) = 1
T(n)= running time on two sublists + linear time on parititioning.
T(n)=T(i) + T(n-i-1) +cn
Worst-Case: pivot is smallest element and i=0.
T(n)=T(0) + T(n-1) +cn which is approximately T(n-1) + cn
Telescope:
T(n-1) = T(n-2) + c(n-1)
T(n-2) = T(n-3) + c(n-2)
T(2) = T(1) + c(2)
Quicksort: Analysis [156]
Best-Case: pivot is in middle and i=n/2.
T(n) = T(n/2) + T(n/2) + cn=2T(n/2)+cn
Divide by n:
Telescope:
...
There are log n of the above equations.
Quicksort: Analysis [157]
Average-Case: each i=0..n-1 is equally likely with probability 1/n.
Similarly:
Subtract:
nT(n)-(n-1)T(n-1) = 2 T(n-1) + 2cn-c
Rearrange and drop -c:
nT(n) = (n+1)T(n-1) + 2cn
Divide by n(n+1):
Quicksort: Analysis [158]
Telescope:
...
Summing:
T(n) = O( )