83
Divide and Conquer Merge and Quick

Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Embed Size (px)

DESCRIPTION

 Recursive in structure  Divide ▪ The problem into sub-problems that are similar to the original but smaller in size.  Conquer ▪ The sub-problems by solving them recursively. ▪ If they are small enough, just solve them in a straightforward manner.  Combine ▪ The solutions to create a solution to the original problem

Citation preview

Page 1: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Divide and ConquerMerge and Quick

Page 2: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Divide and Conquer

Nothing is particularly hard if you divide it into small jobs.Henry Ford

Page 3: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Divide and Conquer

Recursive in structure Divide

▪ The problem into sub-problems that are similar to the original but smaller in size.

Conquer▪ The sub-problems by solving them recursively. ▪ If they are small enough, just solve them in a

straightforward manner. Combine

▪ The solutions to create a solution to the original problem

Page 4: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Merge Sort

Page 5: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Merge Sort

Divide array into two halves, recursively sort left and right halves, then merge two halves Mergesort

To sort an array A[p . . r]: Divide

Divide the n-element sequence to be sorted into two subsequences of n/2 elements each

Conquer Sort the subsequences recursively using merge sort When the size of the sequences is 1 there is nothing more to do

Combine Merge the two sorted subsequences to produce the sorted

answer.

Page 6: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Divide it in two at the midpoint Conquer each side in turn (by

recursively sorting) Merge two halves together

8 2 9 4 5 3 1 6

Merge Sort Approach

Page 7: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Merge Sort Algorithmvoid Mergesort (int A[], int first,

int last)

{

if(first < last)

{

int mid = (first +

last)/2; Mergesort(A, first,

mid); Mergesort(A, mid+1,last);

Merge(A, first, mid,

last);

}

}

0 1 2 3 4 5 6 7

62317425

first lastmid

Check for base case

Divide Conquer ConquerCombine

Page 8: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Merge Sort – Example

18 26 32 6 43 15 9 1

18 26 32 6 43 15 9 1

18 26 32 6 43 15 9 1

2618 6 32 1543 1 9

18 26 32 6 43 15 9 1

18 26 32 6 43 15 9 1

18 26 32 6 15 43 1 9

6 18 26 32 1 9 15 43

1 6 9 15 18 26 32 43

18 26

18 26

18 26

32

32

6

6

32 6

18 26 32 6

43

43

15

15

43 15

9

9

1

1

9 1

43 15 9 1

18 26 32 6 43 15 9 1

18 26 6 32

6 26 3218

1543 1 9

1 9 15 43

1 6 9 1518 26 32 43

Original Sequence Sorted Sequence

Page 9: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

j

Merge Function

6 8 26 32 1 9 42 43

A

k

6 8 26 32 1 9 42 43

k k k k k k

i i i i i j j j j

6 8 26 32 1 9 42 43

1 6 8 9 26 32 42 43

k

B C

void Merge(int A[], int first, int mid, int last)

{ int n1, n2, i, j, k; n1 = mid - first + 1; n2 = last - mid;

int B[n1]; int C[n2];

for (i=0; i< n1;i++) B[i] = A[first +i];

for (j=0; j< n2;j++) C[j] = A[mid +j+1];

i = 0; j = 0; k = first; while(i< n1 && j < n2) { if( B[i] <= C[j]) { A[k] = B[i];

i= i+1; } else { A[k] = C[j];

j= j+1; } k= k+1; } if(i < n1) { while(i < n1) { A[k] = B[i];

k =k+1; i=i+1; }

{ if(j < n2) { while(j < n2) { A[k] = C[j];

k =k+1; j=j+1; }

}}

first mid last

Declaring auxiliary arrays of

size n1 and n2

Moving element to auxiliary

arrays

Compare two elements, move one of them to the original array constant cost.

k

Copying the remaining elements to original array.

Page 10: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

j

Analysis of Merge Function

6 8 26 32 1 9 42 43

A

k

6 8 26 32 1 9 42 43

k k k k k k

i i i i i j j j j

6 8 26 32 1 9 42 43

1 6 8 9 26 32 42 43

k

B C

void Merge(int A[], int first, int mid, int last)

{ int n1, n2, i, j, k; n1 = mid - first + 1; n2 = last - mid;

int B[n1]; int C[n2];

for (i=0; i< n1;i++) B[i] = A[first +i];

for (j=0; j< n2;j++) C[j] = A[mid + j+1];

i = 0; j = 0; k = first; while(i< n1 && j < n2) { if( B[i] <= C[j]) { A[k] = B[i];

i= i+1; } else { A[k] = C[j];

j= j+1; } k= k+1; } if(i < n1) { while(i < n1) { A[k] = B[i];

k =k+1; i=i+1; }

{ if(j < n2) { while(j < n2) { A[k] = C[j];

k =k+1; j=j+1; }

}}

first mid last

Declaring auxiliary arrays of

size n1 and n2

O(1)

Moving element to auxiliary

arraysO(n)

O(1)

Compare two elements, move one of them to the original array constant cost.

k

Copying the remaining elements to original array.

O(n)

O(n)

Page 11: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Merge Sort So far we have seen that it takes

O(n) time to merge two subarrays of size n/2 O(n) time to merge four subarrays of size n/4

into two subarrays of size n/2 O(n) time to merge eight subarrays of size n/8

into four subarrays of size n/4 Etc.

How many levels deep do we have to proceed?

How many times can we divide an array of size n into two halves?

O(log n)

Page 12: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Merge Sort… So if our recursion goes log n levels deep... ...and we do O(n) work at each level... ...our total time is: log n * O(n) ... ...or in other words,

O(n log n) For large arrays, this is much better than

Bubblesort, Selection sort, or Insertion sort, all of which are O(n2)

Not in place Mergesort does, however, require a “workspace”

array as large as our original array (O(n) extra space)

Page 13: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Merge Sortvoid Mergesort (int A[], int

first, int last)

{

if(first < last)

{

int mid = (first + last)/2;

Mergesort(A, first, mid);

Mergesort(A, mid+1,last);

Merge(A, first, mid, last);

}

}

Check for base case

Divide Conquer ConquerCombine

Running time T(n) of Merge Sort:

computing the middle takes (1)

(1)

solving 2 sub-problems takes 2T(n/2)

merging n elements takes (n)

Total:T(n) = (1) if n = 1

T(n) = 2T(n/2) + (n) if n > 1

T(n) = (n lg n)

Page 14: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Recursion Tree – Example Running time of Merge Sort:

T(n) = (1) if n = 1T(n) = 2T(n/2) + (n) if n > 1

Rewrite the recurrence asT(n) = c if n = 1

T(n) = 2T(n/2) + cn if n > 1c > 0: Running time for the base case and time per array element for the divide and combine steps.

Page 15: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Recursion Tree for Merge Sort

For the original problem, we have a cost of cn, plus two subproblems each of size (n/2) and running time T(n/2).

cn

T(n/2) T(n/2)

Each of the size n/2 problems has a cost of cn/2 plus two subproblems, each costing T(n/4). cn

cn/2 cn/2

T(n/4) T(n/4) T(n/4) T(n/4)

Cost of divide and merge.

Cost of sorting subproblems.

Page 16: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Comp 122

Recursion Tree for Merge SortContinue expanding until the problem size reduces to 1.

cn

cn/2 cn/2

cn/4 cn/4 cn/4 cn/4

c c c cc c

lg n

cn

cn

cn

cnTotal : cnlgn+cn

Page 17: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Recursion Tree for Merge Sort

Continue expanding until the problem size reduces to 1.cn

cn/2 cn/2

cn/4 cn/4 cn/4 cn/4

c c c cc c

•Each level has total cost cn.•Each time we go down one level, the number of subproblems doubles, but the cost per subproblem halves cost per level remains the same.•There are lg n + 1 levels, height is lg n. (Assuming n is a power of 2.)• Can be proved by induction.

•Total cost = sum of costs at each level = (lg n + 1)cn = cnlgn + cn = (n lgn).

Page 18: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quick Sort

Page 19: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quick Sort

Partition array into items that are “small” and items that are “large”, then recursively sort the two sets Quicksort

Divide Partition array into left and right sub-arrays

▪ Choose an element of the array, called pivot▪ The elements in left sub-array are all less than pivot▪ Elements in right sub-array are all greater than pivot

Conquer Recursively sort left and right sub-arrays

Combine Trivial: the arrays are sorted in place No additional work is required to combine them The entire array is now sorted

Page 20: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quick Sort…A key step in the Quicksort algorithm

is partitioning the array We choose some (any) number p in the

array to use as a pivot We partition the array into three parts:

p

numbers less than p

numbers greater than or equal to p

p

Page 21: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quick Sort Approach

1381

9243

65

31 57

26

750

S select pivot value

13 819243 65

31

5726

750S1 S2partition S

13 4331 57260

S1

81 927565

S2QuickSort(S1)

13 4331 57260 65 81 9275S S is sorted

QuickSort(S2)

Page 22: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quick Sort Algorithmvoid quicksort(int array[], int left, int

right) { if (left < right) {

int p = partition(array, left, right); quicksort(array, left, p - 1);

quicksort(array, p + 1, right);}

}

Check for base case

Divide

Conquer

Conquer

Combine

Page 23: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

The Partition method int partition(int a[], int left, int right) { int p = a[left], l = left + 1, r = right; while (l < r)

{ while (l < right && a[l] < p)

l++; while (r > left && a[r] >= p)

r--; if (l < r)

{ swap(a[l], a[r]); } } a[left] = a[r]; a[r] = p; return r; }

Page 24: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

ExampleWe are given array of n integers to

sort:40 20 10 80 60 50 7 30 100

Page 25: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Pick Pivot ElementThere are a number of ways to pick the pivot

element. In this example, we will use the first element in the array:

40 20 10 80 60 50 7 30 100

Page 26: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Partitioning ArrayGiven a pivot, partition the elements of

the array such that the resulting array consists of:

1. One sub-array that contains elements < pivot

2. Another sub-array that contains elements >= pivot

The sub-arrays are stored in the original data array.

Partitioning loops through, swapping elements below/above pivot.

Page 27: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

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

l r

1. while (l<r)

Page 28: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

l r

1. while (l<r) 2. { while (l < right && a[l] < p )

l ++;

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

Page 29: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

l r

1. while (l<r) 2. { while (l < right && a[l] < p )

l ++;

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

Page 30: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

l r

1. while (l<r) 2. { while (l < right && a[l] < p )

l ++;

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

Page 31: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

l r

1. while(l<r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;

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

Page 32: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

l r

1. while(l<r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;

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

Page 33: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 80 60 50 7 30 100pivot_index = 0

l r

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

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

Page 34: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

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

Page 35: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

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

Page 36: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

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

Page 37: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 38: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 39: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 40: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 60 50 7 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 41: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 42: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 43: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 44: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 45: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 46: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 47: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 48: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 49: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 50: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

Page 51: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

40 20 10 30 7 50 60 80 100pivot_index = 0

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

a[left] = a[r]; a[r] = p; return r;

Page 52: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

7 20 10 30 40 50 60 80 100pivot_index = 4

l r

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

1. while (l < r)2. { while (l < right && a[l] < p)

l ++;3. while (r > left &&a[r] >= p)

r--;4. if ( l < r )

swap ( a[l] , a[r] )}

a[left] = a[r]; a[r] = p; return r;

Page 53: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Partition Result

7 20 10 30 40 50 60 80 100

<= pivot > pivot

Page 54: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Recursion: Quicksort Sub-arrays

7 20 10 30 40 50 60 80 100

< data[pivot] > = data[pivot]

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

Page 55: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Example of partitioning

choose pivot: 4 3 6 9 2 4 3 1 2 1 8 9 3 5 6 search: 4 3 6 9 2 4 3 1 2 1 8 9 3 5 6 swap: 4 3 3 9 2 4 3 1 2 1 8 9 6 5 6 search: 4 3 3 9 2 4 3 1 2 1 8 9 6 5 6 swap: 4 3 3 1 2 4 3 1 2 9 8 9 6 5 6 search: 4 3 3 1 2 4 3 1 2 9 8 9 6 5 6 swap: 4 3 3 1 2 2 3 1 4 9 8 9 6 5 6 search: 4 3 3 1 2 2 3 1 4 9 8 9 6 5 6 (left > right) swap with pivot: 1 3 3 1 2 2 3 4 4 9 8 9 6 5 6

Page 56: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Partition method int partition(int a[], int left, int right) { int p = a[left], l = left + 1, r = right; while (l < r)

{ while (l < right && a[l] < p)

l++; while (r > left && a[r] >= p)

r--; if (l < r)

{ swap(a[l], a[r]); } } a[left] = a[r]; a[r] = p; return r; }

O(n)

Page 57: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Quick Sortvoid quicksort(int array[], int left, int

right) { if (left < right) {

int p = partition(array, left, right); quicksort(array, left, p - 1);

quicksort(array, p + 1, right);}

}

Check for base case

Divide

Conquer

Conquer

Combine

(1)

(n)

(0)

Page 58: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Best Case

Suppose each partition operation divides the array almost exactly in half

Then the depth of the recursion in log2n.At each level of the recursion, all the

partitions at that level do work that is linear in n.

O(log2n) * O(n) = O(n log2n) Hence in the average case, quicksort

has time complexity O(n log2n)

Page 59: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Partitioning at various levels-Best Case

Page 60: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Quick Sort- Best Casevoid quicksort(int array[], int left, int

right) { if (left < right) {

int p = partition(array, left, right); quicksort(array, left, p - 1);

quicksort(array, p + 1, right);}

}

Check for base case

Divide

Conquer

Conquer

Combine

(1)

(n)

(0)

Total:T(n) = (1) if n = 1

T(n) = 2T(n/2) + (n) if n > 1

T(n) = (n lg n)

2T(n/2)

Page 61: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Best Case Partitioning Best-case partitioning

Partitioning produces two regions of size n/2 Recurrence: q=n/2

T(n) = 2T(n/2) + (n)T(n) = (nlgn) (Master theorem)

Page 62: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Worst case

In the worst case, partitioning always divides the size n array into these three parts: A length one part, containing the pivot

itself A length zero part, and A length n-1 part, containing everything

elseWe don’t recur on the zero-length

partRecurring on the length n-1 part

requires (in the worst case) recurring to depth n.

Page 63: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Worst Case Call Tree N=4

Page 64: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Worst Case Partitioning Worst-case partitioning

One region has zero element and the other has n – 1 elements

Maximally unbalanced Recurrence: q=1

T(n) = T(n – 1) = T(0) + (n) T(1) = (1)T(n) = T(n – 1) + n

nn - 1

n - 2n - 3

21

00

0

0

0n

nn - 1n - 2n - 3

21

(n2)

When does the worst case happen?

Page 65: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Analysis of Quick Sort- Worst Casevoid quicksort(int array[], int left, int

right) { if (left < right) {

int p = partition(array, left, right); quicksort(array, left, p - 1);

quicksort(array, p + 1, right);}

}

Check for base case

Divide

Conquer

Conquer

Combine

(1)

(n)

(0)

Total:

T(n) = (1) if n = 1T(n) = T(n-1) (n) if n > 1

T(n) = (n 2)

T(n-1)+T(0)=T(n-1)

Page 66: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quicksort: Worst Case

Assume first element is chosen as pivot.

Assume we get array that is already in order:

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_index too_small_index

Page 67: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_index too_small_index

Page 68: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_indextoo_small_index

Page 69: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_index too_small_index

Page 70: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_index too_small_index

Page 71: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_index too_small_index

Page 72: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

too_big_index too_small_index

Page 73: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

1. While data[too_big_index] <= data[pivot]++too_big_index

2. While data[too_small_index] > data[pivot]--too_small_index

3. If too_big_index < too_small_indexswap data[too_big_index] and data[too_small_index]

4. While too_small_index > too_big_index, go to 1.5. Swap data[too_small_index] and data[pivot_index]

2 4 10 12 13 50 57 63 100pivot_index = 0

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

> data[pivot]<= data[pivot]

Page 74: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Case Between Worst and Best 9-to-1 proportional split

Q(n) = Q(9n/10) + Q(n/10) + n

Page 75: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Average-Case Analysis

Assume Each of the sizes for S1 is equally likely

This assumption is valid for our pivoting (median-of-three) strategy

On average, the running time is O(N log N)

Page 76: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quicksort Analysis Assume that keys are random, uniformly

distributed. Best case and average case running time:

O(n log n) Worst case running time?

Recursion:1. Partition splits array in two sub-arrays:

• one sub-array of size 0• the other sub-array of size n-1

2. Quicksort each sub-array Depth of recursion tree? O(n) Number of accesses per partition? O(n)

Page 77: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Quicksort Analysis

Assume that keys are random, uniformly distributed.

Best case running time: O(n log n) Average case running time: O(n log n) Worst case running time: O(n2)!!!

What can we do to avoid worst case?

Page 78: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Improved Pivot SelectionPick median value of three elements from

data array:data[0], data[n/2], and data[n-1].

Use this median value as pivot.

8 1 4 9 0 3 5 2 7 6

0 1 2 3 4 5 6 7 8 9

6 1 4 9 0 3 5 2 6 8

Median of 0, 6, 8 is 6. Pivot is 6

Choose the pivot as the median of three

Page 79: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Partitioning: Choosing the pivot One implementation (there are others)

median3 finds pivot and sorts left, center, right▪ Median3 takes the median of leftmost, middle,

and rightmost elements▪ An alternative is to choose the pivot randomly

(need a random number generator; “expensive”)▪ Another alternative is to choose the first element

(but can be very bad. Why?) Swap pivot with the first element

Page 80: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Summary of QuicksortBest case: split in the middle — Θ( n

log n) Worst case: sorted array! — Θ( n2) Average case: random arrays — Θ( n

log n)Memory requirement? In-place sorting algorithmConsidered as the method of choice

for internal sorting for large files (n ≥ 10000)

Page 81: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Summary of Quicksort Best case: split in the middle — Θ( n log n) Worst case: sorted array! — Θ( n2) Average case: random arrays — Θ( n log n) Considered as the method of choice for internal

sorting for large files (n ≥ 10000) Improvements:

better pivot selection: median of three partitioning avoids worst case in sorted files

switch to insertion sort on small subfiles elimination of recursionthese combine to 20-25% improvement

Page 82: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Small arrays

For very small arrays, quicksort does not perform as well as insertion sort how small depends on many factors,

such as the time spent making a recursive call, the compiler, etc

Do not use quicksort recursively for small arrays Instead, use a sorting algorithm that is

efficient for small arrays, such as insertion sort

Page 83: Nothing is particularly hard if you divide it into small jobs. Henry Ford Nothing is particularly hard if you divide it into small jobs. Henry Ford

Properties of Quicksort

Not stable because of long distance swapping.

No iterative version (without using a stack).

Pure quicksort not good for small arrays. “In-place”, but uses auxiliary storage

because of recursive call (O(logn) space). O(n log n) average case performance, but

O(n2) worst case performance.