recursive sorting: quicksort and its complexity. sorting card players all know how to sort … first...

71
Recursive sorting: Quicksort and its Complexity

Upload: ashley-oliver

Post on 18-Dec-2015

236 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Recursive sorting: Quicksort and its Complexity

Page 2: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Sorting• Card players all know how to sort …

• First card is already sorted

• With all the rest,Scan back from the end until you find the first card larger

than the new one,Move all the lower ones up one slot insert it

Q

2

9

A

K

10

J

2

2

9

Page 3: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Sorting - Insertion sort

• Complexity• For each card

• Scan O(n)

• Shift up O(n)

• Insert O(1)

• Total O(n)• First card requires O(1), second O(2), …

• For n cards operations O(n2) ii=1

n

Page 4: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Sorting - Insertion sort

• Complexity• For each card

• Scan O(n) O(log n)

• Shift up O(n)

• Insert O(1)

• Total O(n)• First card requires O(1), second O(2), …

• For n cards operations O(n2) ii=1

n

Unchanged!Because the

shift up operationstill requires O(n)

time

Use binary search!

Page 5: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Insertion Sort - Implementation

• A challenge for you• The code in the notes (and on the Web) has an error• First person to email a correct version

gets up to 2 extra marks added to their final mark if that would move them up a grade!

• ie if you had x8% or x9%, it goes to (x+1)0%

• To qualify, you need to point out the error in the original, as well as supply a corrected version!

Page 6: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Sorting - Bubble

• From the first element• Exchange pairs if they’re out of order

• Last one must now be the largest• Repeat from the first to n-1• Stop when you have only one element to check

Page 7: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Bubble Sort - Analysis/* Bubble sort for integers */

#define SWAP(a,b) { int t; t=a; a=b; b=t; }

void bubble( int a[], int n ) {

int i, j;

for(i=0;i<n;i++) { /* n passes thru the array */

/* From start to the end of unsorted part */

for(j=1;j<(n-i);j++) {

/* If adjacent items out of order, swap */

if( a[j-1]>a[j] ) SWAP(a[j-1],a[j]);

}

}

}

O(1) statement

Page 8: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Bubble Sort - Analysis/* Bubble sort for integers */

#define SWAP(a,b) { int t; t=a; a=b; b=t; }

void bubble( int a[], int n ) {

int i, j;

for(i=0;i<n;i++) { /* n passes thru the array */

/* From start to the end of unsorted part */

for(j=1;j<(n-i);j++) {

/* If adjacent items out of order, swap */

if( a[j-1]>a[j] ) SWAP(a[j-1],a[j]);

}

}

}

Inner loopn-1, n-2, n-3, … , 1 iterations

O(1) statement

Page 9: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Bubble Sort - Analysis/* Bubble sort for integers */

#define SWAP(a,b) { int t; t=a; a=b; b=t; }

void bubble( int a[], int n ) {

int i, j;

for(i=0;i<n;i++) { /* n passes thru the array */

/* From start to the end of unsorted part */

for(j=1;j<(n-i);j++) {

/* If adjacent items out of order, swap */

if( a[j-1]>a[j] ) SWAP(a[j-1],a[j]);

}

}

}

Outer loop n iterations

Page 10: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Bubble Sort - Analysis/* Bubble sort for integers */

#define SWAP(a,b) { int t; t=a; a=b; b=t; }

void bubble( int a[], int n ) {

int i, j;

for(i=0;i<n;i++) { /* n passes thru the array */

/* From start to the end of unsorted part */

for(j=1;j<(n-i);j++) {

/* If adjacent items out of order, swap */

if( a[j-1]>a[j] ) SWAP(a[j-1],a[j]);

}

}

}

Overall

ii=n-1

1=

n(n+1)2

= O(n2)

n outer loop iterations inner loop iteration count

Page 11: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Sorting - Simple

• Bubble sort • O(n2)

• Very simple code

• Insertion sort• Slightly better than bubble sort

• Fewer comparisons• Also O(n2)

• But HeapSort is O(n log n)

• Where would you use bubble or insertion sort?

Page 12: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Simple Sorts• Bubble Sort or Insertion Sort

• Use when n is small

• Simple code compensates for low efficiency!

n^2 and n log n

0

500

1000

1500

2000

2500

0 10 20 30 40 50 60

n

Tim

e n log n

n 2̂

Page 13: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Recursive Array ProgrammingRecursive Array Programming

Recursive function definitions assume that a function works for a smaller value.

With arrays, “a smaller value” means a shorter array, i.e., a subarray, contiguous elements from the original array

We’ll define a recursive function over an array by using the same function over a subarray, and a base case

Subscripts will mark the lower and upper bounds of the subarrays

Page 14: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Subarrays

myArray

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]

Base case

1 through 92 through 9

3 through 94 through 9

5 through 9

6 through 9

7 through 98 through 9

9 through 9

Page 15: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Example: Recursively find sum of array elements, A[lo] to A[hi]

• Assume sum( ) properly returns sum of elements for a smaller array of doubles

• Then we could write:

double sum (double[ ] A, int lo, int hi) {return ( A[lo] + sum(A, lo + 1, hi) );

}

• But we’re not done; what’s the base case?

Page 16: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Base Case is when Subarray is Empty, hi is less than lo

double sum (double[ ] A, int lo, int hi) {if (hi < lo)

return 0.0;else

return ( A[lo] + sum(A, lo + 1, hi) );}

• Yes, we could have defined this using(hi == lo) as the base case…

Page 17: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Recursive Sorting Algorithms

• We can use this same idea of recursive functions over subarrays to rewrite our sorting algorithms

• Let’s see how this works for selection sort, insertion sort, and then some new sorting algorithms

Page 18: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

More Recursive Sorting: QuicksortMore Recursive Sorting: Quicksort

• Quicksort is an O(n2) algorithm in the worst case, but its running time is usually proportional to n log2 n;it is the method of choice for many sorting jobs

• We’ll first look at the intuition behind the algorithm: take an array

V O N I C AE Rarrange it so the small values are on the left half and all the big values are on the right half:

V O NI C AE R

Page 19: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort IntuitionQuicksort Intuition

• Then, do it again:

VO NIC A E Rand again:

VONICA E RThe divide-and-conquer strategy will, in general, take log2n steps to move the A into position. The intuition is that, doing this for n elements, the whole algorithm will take O(n log2 n) steps.

Page 20: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

What Quicksort is really doingWhat Quicksort is really doing

1. Partition array a into smaller elements and larger ones – smaller ones from a[0]…a[m-1] (not necessarily in order), larger ones in positions a[m+1]…a[length-1] (not necessarily in order), and the “middle” element in a[m]. So a:

5 3 11 7192712 18might be partitioned (with m=4) as: 5 3 117 192712 18

pivotsmaller than pivot larger than pivot

Page 21: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

quicksort(), next 2 steps

2. Recursively sort a[0]…a[m-1]. Our a becomes:

53 117 192712 18

3. Recursively sort a[m+1]…a[length-1]. Our a becomes:

53 117 19271218

pivot

pivot

Page 22: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

quicksort( )

private void quicksort(double[] a, int lo, int hi) {int m;

if (hi > lo+1) { // there are at least 3 elements// so sort recursively

m = partition(a, lo, hi);quicksort(a, lo, m-1);quicksort(a, m+1, hi);

}else // the base case…

…}

Page 23: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

The base case…

• We have the base case in this recursion when the subarray a[lo]…a[hi] contains zero elements (lo==hi+1), one element (lo==hi), or two elements (lo==hi-1).

• With no elements, we ignore it• With one element, it’s already sorted• With two elements, we just (might) swap them:

// 0, 1, or 2 elements, so sort directlyif (hi == lo+1 && a[lo] > a[hi])

swap(a, lo, hi);

Page 24: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

quicksort( )

private void quicksort(double[] a, int lo, int hi) {int m;

if (hi > lo+1) { // there are at least 3 elements// so sort recursively

m = partition(a, lo, hi);quicksort(a, lo, m-1);quicksort(a, m+1, hi);

}else // 0, 1, or 2 elements, so sort directly

if (hi == lo+1 && a[lo] > a[hi])swap(a, lo, hi);

}

Page 25: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

The outside world’s version

• As usual, we overload quicksort( ) and provide a public version that “hides” the last two arguments:

public void quicksort(double[] a) {quicksort(a, 0, a.length-1);

}

Page 26: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Now, all we need is partition( )

int partition(double[] a, int lo, int hi) {// Choose middle element among a[lo]…a[hi],// and move other elements so that a[lo]…a[m-1]// are all less than a[m] and a[m+1]…a[hi] are// all greater than a[m]//// m is returned to the caller

}

Page 27: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Are you feeling lucky?• Now, this would work great if we knew the median value in

the array segment, and could choose it as the pivot (i.e., know which small values go left and which large values go right). But we can’t know the median value without actually sorting the array!

• So instead, we somehow pick a pivot and hope that it is near median.

• If the pivot is the worst choice (each time), the algorithm becomes O(n2). If we are roughly dividing the subarrays in half each time, we get an O(nlog2n) algorithm.

Page 28: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

How to pick the median value

• There are many techniques for doing this. In practice, one good way of choosing the pivot is to take the median of three elements, specifically a[lo+1], a[(lo+hi)/2], and a[hi]

• We’ll choose their median using the method medianLocation( )

Page 29: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

medianLocation( )

int medianLocation(double[] a,int i, int j, int

k) {if (a[i] <= a[j])

if (a[j] <= a[k])return j;

else if (a[i] <= a[k])return k;

else return i;else // a[j] < a[i]

if (a[i] <= a[k])return i;

else if (a[j] <= a[k])return k;

else return j;}

Page 30: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

The partitioning process

• From the three red elements, we choose the median, a[hi]

8 1 2 6 34 9 10 5 7lo+1 hi(lo+hi)/2

• We swap the median with a[lo], and start the partitioning process on the rest:7 1 2 6 34 9 10 5 8

lo+1 hi(lo+hi)/2

Page 31: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

The partitioning process

• We shuffle around elements from a[lo+1] to a[hi] so that all elements less than the pivot (a[lo]) appear to all the elements greater than the pivot

• Until we are done, we have no way of knowing how many elements are less and how many elements are greater

7 1 2 6 104 5 3 8 9lo+1 hi(lo+hi)/2 m

Page 32: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

The partitioning process

• m is the largest subscript that contains a value less than the pivot

• We have discovered (in our example) that m = 6

• We then swap a[m] with a[lo], placing the pivot in its rightful position, in a[m], then continue to sort the left and right subarrays recursively

7 1 2 6 104 5 3 8 9lo+1 hi(lo+hi)/2 m

6 1 2 7 104 5 3 8 9lo+1 hi(lo+hi)/2 mlo

pivot

Page 33: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

How partition( ) works

• We will use a 3-argument method:int partition(double[ ] a, int lo, int hi)

and a 4-argument method:int shuffle(double[ ] a, int lo,

int hi, double pivot)

• The 3-argument “partition” moves the pivot element into a[lo], calls the 4-argument method “shuffle” to shuffle the elements in the subarray a[lo+1]…a[hi], then swaps a[lo] into a[m] and returns m

Page 34: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

3-argument partition( )

int partition(double[] a, int lo, int hi) {// Choose middle element among a[lo]…a[hi],// and move other elements so that a[lo]…a[m-1]// are all less than a[m] and a[m+1]…a[hi] are// all greater than a[m]//// m is returned to the callerswap(a, lo, medianLocation(a, lo+1, hi, (lo+hi)/2));int m = shuffle(a, lo+1, hi, a[lo]);swap(a, lo, m);return m;

}

Page 35: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

How the 4-argument shuffle( ) works

• The 4-argument method does the main work, recursively calling itself on subarrays

• We of course assume shuffle( ) works on any smaller array…

• If the first element of the array is less than or equal to pivot, it’s already in the right place, just call shuffle( ) recursively on the rest:

if (a[lo] <= pivot) // a[lo] in correct half

return shuffle(a, lo+1, hi, pivot);

(this is the “current” lo, not the lo of the whole array)

Page 36: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

How the 4-argument shuffle( ) works

• If a[lo] > pivot, then a[lo] belongs in the upper half of the subarray, and we swap it with a[hi]

• We still don’t know where the new a[lo] value should go, so we shuffle recursively on the subarray that includes a[lo] but not a[hi]

if (a[lo] <= pivot) // a[lo] in correct half

return shuffle(a, lo+1, hi, pivot);else { // a[lo] in wrong half

swap(a, lo, hi);return shuffle(a, lo, hi-1, pivot);

}

Page 37: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

How the 4-argument shuffle( ) works

• The base case is the one element subarray, when lo==hi. Is the one element “small” or “large”?

• If it is small (less than the pivot), then it is at the middle point m (and we can swap it with the pivot)

• Otherwise, it is just above the middle point (and we want the pivot swapped with the element just below it)

Page 38: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

4-argument shuffle( )

int shuffle(double[] a, int lo, int hi, double pivot) {if (hi == lo)

if (a[lo] < pivot)return lo;

elsereturn lo-1;

else if (a[lo] <= pivot) // a[lo] in correct halfreturn shuffle(a, lo+1, hi, pivot);

else { // a[lo] in wrong halfswap(a, lo, hi);return shuffle(a, lo, hi-1, pivot);

}}

Page 39: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Example of partition

• The starting array:

4 2 15 3 6lo hi

• Choose the median from among:

4 2 15 3 6lo hi(lo+hi)/2lo+1

• Swap the median with a[lo]:

3 2 15 4 6pivot

Page 40: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Example of partition

• Now, shuffle the subarray (not counting pivot); a is now the new subarray…

3 2 15 4 6lo hi

• a[lo] > pivot, so swap it with a[hi], and continue with the shuffle:

3 2 51 4 6lo hi

pivot

pivot

Page 41: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Example of partition

• a[lo] is now less than pivot, so we leave it and continue with the shuffle:

3 2 51 4 6lo hi

• Now a[lo] is greater than pivot, so we swap it with a[hi] and continue with the shuffle:

3 2 51 6 4lo hi

pivot

pivot

Page 42: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Example of partition

• a[lo] is again greater than pivot, so we swap it with a[hi] and continue with the shuffle:

3 6 51 2 4lo hipivot

• a[lo] is less than the pivot, so lo (i.e., index 2) is returned by the 4-argument shuffle( ); the 3-argument partition( ) then swaps the pivot and the middle element, also returning index 2

3 6 512 4pivot

Now we’re ready to recursively quicksort the left and right subarrays

Page 43: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

quicksort( )

private void quicksort(double[] a, int lo, int hi) {int m;

if (hi > lo+1) { // there are at least 3 elements// so sort recursively

m = partition(a, lo, hi);quicksort(a, lo, m-1);quicksort(a, m+1, hi);

}else // 0, 1, or 2 elements, so sort directly

if (hi == lo+1 && A[lo] > A[hi])swap(a, lo, hi);

}

Page 44: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Performance of QuickSort

• Quicksort:

• Best case: O(nlog2n)

• Worst case: O(n2) – when the pivot is always the second-largest or second-smallest element (since medianLocation won’t let us choose the smallest or largest)

• Average case over all possible arrangements of n array elements: O(nlog2n)

Page 45: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort

• Efficient sorting algorithm• Discovered by C.A.R. Hoare

• Example of Divide and Conquer algorithm• Two phases

• Partition phase• Divides the work into half

• Sort phase• Conquers the halves!

Page 46: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort

• Partition• Choose a pivot• Find the position for the pivot so that

• all elements to the left are less• all elements to the right are greater

< pivot > pivotpivot

Page 47: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort

• Conquer• Apply the same algorithm to each half

< pivot > pivot

pivot< p’ p’ > p’ < p” p” > p”

Page 48: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort

• Implementation

quicksort( void *a, int low, int high ) { int pivot; /* Termination condition! */ if ( high > low ) { pivot = partition( a, low, high ); quicksort( a, low, pivot-1 ); quicksort( a, pivot+1, high ); } }

Divide

Conquer

Page 49: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high; while ( left < right ) { /* Move left while item < pivot */ while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */ while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

This exampleuses int’s

to keep thingssimple!

23 12 15 38 42 18 36 29 27

low high

Any item will do as the pivot,choose the leftmost one!

Page 50: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high; while ( left < right ) { /* Move left while item < pivot */ while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */ while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

Set left and right markers

23 12 15 38 42 18 36 29 27

low highpivot: 23

left right

Page 51: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high;

while ( left < right ) { /* Move left while item < pivot */ while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */ while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

Move the markers until they cross over

23 12 15 38 42 18 36 29 27

low highpivot: 23

left right

Page 52: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high;

while ( left < right ) { /* Move left while item < pivot */ while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */ while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

Move the left pointer whileit points to items <= pivot

23 12 15 38 42 18 36 29 27

low highpivot: 23

left right Move right similarly

Page 53: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high;

while ( left < right ) { /* Move left while item < pivot */

while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */

while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

Swap the two itemson the wrong side of the pivot

23 12 15 38 42 18 36 29 27

low highpivot: 23

left right

Page 54: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high;

while ( left < right ) { /* Move left while item < pivot */

while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */

while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

left and right have swapped over,

so stop

23 12 15 18 42 38 36 29 27

low highpivot: 23

leftright

Page 55: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high;

while ( left < right ) { /* Move left while item < pivot */

while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */

while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

Finally, swap the pivotand right

23 12 15 18 42 38 36 29 27

low highpivot: 23

leftright

Page 56: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Partition

int partition( int *a, int low, int high ) { int left, right; int pivot_item; pivot_item = a[low]; pivot = left = low; right = high;

while ( left < right ) { /* Move left while item < pivot */

while( a[left] <= pivot_item ) left++; /* Move right while item > pivot */

while( a[right] >= pivot_item ) right--; if ( left < right ) SWAP(a,left,right); } /* right is final position for the pivot */ a[low] = a[right]; a[right] = pivot_item; return right; }

Return the positionof the pivot

18 12 15 23 42 38 36 29 27

low high

pivot: 23right

Page 57: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Conquer

pivot

18 12 15 23 42 38 36 29 27pivot: 23

Recursivelysort left half

Recursivelysort right half

Page 58: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Analysis

• Partition• Check every item once O(n)

• Conquer• Divide data in half O(log2n)

• Total• Product O(n log n)

• Quicksort is generally faster• Fewer comparisons

• Details later (and assignment 2!)

• But there’s a catch …………….

Page 59: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - The truth!

• What happens if we use quicksorton data that’s already sorted(or nearly sorted)

• We’d certainly expect it to perform well!

Page 60: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - The truth!

• Sorted data

1 2 3 4 5 6 7 8 9

pivot

< pivot

?

> pivot

Page 61: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - The truth!

• Sorted data• Each partition

produces• a problem of size 0• and one of size n-1!

• Number of partitions?

1 2 3 4 5 6 7 8 9

> pivot

2 3 4 5 6 7 8 9

> pivot

pivot

pivot

Page 62: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - The truth!

• Sorted data• Each partition

produces• a problem of size 0• and one of size n-1!

• Number of partitions?• n each needing time O(n)

• Total nO(n) or O(n2)

? Quicksort is as bad as bubble or insertion sort

1 2 3 4 5 6 7 8 9

> pivot

2 3 4 5 6 7 8 9

> pivot

pivot

pivot

Page 63: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - The truth!

• Quicksort’s O(n log n) behaviour• Depends on the partitions being nearly equal there are O( log n ) of them

• On average, this will nearly be the case and quicksort is generally O(n log n)

• Can we do anything to ensure O(n log n) time?

• In general, no• But we can improve our chances!!

Page 64: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Choice of the pivot

• Any pivot will work …• Choose a different pivot …

• so that the partitions are equal• then we will see O(n log n) time

1 2 3 4 5 6 7 8 9

pivot

< pivot > pivot

Page 65: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Median-of-3 pivot

• Take 3 positions and choose the median• say … First, middle, last

median is 5 perfect division of sorted data every time! O(n log n) time

Since sorted (or nearly sorted) data is common,median-of-3 is a good strategy

• especially if you think your data may be sorted!

1 2 3 4 5 6 7 8 9

Page 66: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Random pivot

• Choose a pivot randomly• Different position for every partition On average, sorted data is divided evenly O(n log n) time

• Key requirement• Pivot choice must take O(1) time

Page 67: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Guaranteed O(n log n)?

• Never!!• Any pivot selection strategy

could lead to O(n2) time

• Here median-of-3 chooses 2 One partition of 1 and

• One partition of 7

• Next it chooses 4 One of 1 and

• One of 5

1 4 9 6 2 5 7 8 3

1 2 4 9 6 5 7 8 3

Page 68: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Key Points

• Sorting• Bubble, Insert

• O(n2) sorts• Simple code• May run faster for small n,

n ~10 (system dependent)• Quick Sort

• Divide and conquer• O(n log n)

Page 69: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Key Points

• Quick Sort• O(n log n) but ….

• Can be O(n2)

• Depends on pivot selection• Median-of-3• Random pivot

• Better but not guaranteed

Page 70: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Why bother?

• Use Heapsort instead?• Quicksort is generally faster

• Fewer comparisons and exchanges

• Some empirical data

n Quick Heap InsertComp Exch Comp Exch Comp Exch

100 712 148 2842 581 2595 899200 1682 328 9736 9736 10307 3503500 5102 919 53113 4042 62746 21083

Page 71: Recursive sorting: Quicksort and its Complexity. Sorting Card players all know how to sort … First card is already sorted With all the rest, ¶Scan back

Quicksort - Why bother?

• Reporting data• Normalisation works when you have a hypothesis to work

with!

n Quick Heap Insert nlogn n^2

Comp Exch Norm Comp Exch Norm Comp Exch Norm Norm

100 712 148 0.74 2842 581 2.91 2595 899 4.50 0.09200 1682 328 0.71 9736 1366 2.97 10307 3503 7.61 0.09500 5102 919 0.68 53113 4042 3.00 62746 21083 15.62 0.08

Divide by n log n

Divide by n2