200 likes | 211 Views
CSE 143 Lecture 23. QuickSort Previous slides based on ones by Ethan Apter & Marty Stepp. Merge Sort. Merge sort: divide a list into two halves sort the halves recombine the sorted halves into a sorted whole Merge sort is an example of a “divide and conquer” algorithm
E N D
CSE 143Lecture 23 QuickSort Previous slides based on ones by Ethan Apter & Marty Stepp
Merge Sort • Merge sort: • divide a list into two halves • sort the halves • recombine the sorted halves into a sorted whole • Merge sort is an example of a “divide and conquer” algorithm • divide and conquer algorithm: an algorithm that repeatedly divides the given problem into smaller pieces that can be solved more easily • it’s easier to sort the two small lists than the one big list
Merge Sort Picture split merge
sort Final Code • Final version of sort: public static void sort(int[] list) { if (list.length > 1) { int[] list1 = new int[list.length / 2]; int[] list2 = new int[list.length - list.length / 2]; for (int i = 0; i < list1.length; i++) list1[i] = list[i]; for (int i = 0; i < list2.length; i++) list2[i] = list[i + list1.length]; sort(list1); sort(list2); mergeInto(list, list1, list2); } }
mergeInto Final Code private static void mergeInto(int[] result, int[] list1, int[] list2) { int i1 = 0; int i2 = 0; for (int i = 0; i < result.length; i++) { if (i2 >= list2.length || (i1 < list1.length && list1[i1] <= list2[i2])) { result[i] = list1[i1]; i1++; } else { result[i] = list2[i2]; i2++; } } }
Analyzing Our Performance • Sun’s sort is faster than ours • but only by a factor of about 2.5 • That’s really good! It didn’t take us long to write this, and Sun’s Arrays.sort is a professional sorting method • Sun has had years to fine-tune the performance of Arrays.sort, and yet we wrote a reasonably competitive merge sort in less than an hour! • So what’s the complexity of merge sort?
Complexity of Merge Sort • To determine the time complexity, let’s break our merge sort into pieces and analyze the pieces • Remember, merge sort consists of: • divide a list into two halves • sort the halves • recombine the sorted halves into a sorted whole • Dividing the list in half and recombining the lists are pretty easy to analyze: • both have O(n) time complexity • But what about sorting the halves?
Complexity of Merge Sort • We can think of merge sort as occurring in levels • at the first level, we want to sort the whole list • at the second level, we want to sort the two half lists • at the third level, we want to sort the four quarter lists • ... • We know there’s O(n) work at each level from dividing/recombining the lists • But how many levels are there? • if we can figure this out, our time complexity is just O(n * num_levels)
Complexity of Merge Sort • Because we divide the array in half each time, there are log(n) levels • So merge sort is an O(n log(n)) algorithm • this is a big improvement over the O(n2) sorting algorithms log(n) levels O(n) work at each level
Quick Sort • Pick a “pivot” • Divide into less-than & greater-than pivot • Sort each side recursively
The steps of QuickSort S select pivot value 81 31 57 43 13 75 92 0 26 65 S1 S2 partition S 0 31 75 43 65 13 81 92 26 57 QuickSort(S1) and QuickSort(S2) S1 S2 0 13 26 31 43 57 75 81 92 65 S Presto! S is sorted 0 13 26 31 43 57 65 75 81 92 [Weiss]
Quick sort vs. Merge sort Quick sort: • pick a pivot value from the array • partition the list around the pivot value • sort the left half • sort the right half Merge sort: • divide a list into two identically sized halves • sort the left half • sort the right half • recombine the sorted halves into a sorted whole
QuickSort Example i j 5 1 3 9 7 0 4 2 6 8 j i 5 1 3 9 7 0 4 2 6 8 i j 5 1 3 9 7 0 4 2 6 8 i j 5 1 3 2 7 0 4 9 6 8 • Move i to the right to be larger than pivot. • Move j to the left to be smaller than pivot. • Swap
QuickSort Example i j 5 1 3 2 7 0 4 9 6 8 i j 5 1 3 2 7 0 4 9 6 8 i j 5 1 3 2 4 0 7 9 6 8 i j 5 1 3 2 4 0 7 9 6 8 j i 5 1 3 2 4 0 7 9 6 8 j i 0 1 4 2 4 5 6 9 7 8 pivot S1 < pivot S2 > pivot
Partition private static int partition(Object[] list, int low, int high) { swap(list, low, (low + high) / 2); // swap middle value into first pos Object pivot = list[low]; // remember pivot int index1 = low + 1; // index of first unknown value int index2 = high; // index of last unknown value while (index1 <= index2) { // while some values still unknown if (list[index1] <= pivot) index1++; else if (list[index2] > pivot) index2--; else { swap(list, index1, index2); index1++; index2--; } } swap(list, low, index2); // put the pivot value between the two // sublists and return its index return index2; }
Quicksort public static void sort(Object[] list, int low, int high){ if (low < high) { int pivotIndex = partition(list, low, high); sort(list, low, pivotIndex - 1); sort(list, pivotIndex + 1, high); } }
Optimized Quicksort • Quicksort(A[]: integer array, left, right : integer): { • pivotindex : integer; • if left + CUTOFF right then • pivot := median3(A,left,right); • pivotindex := Partition(A,left,right-1,pivot); • Quicksort(A, left, pivotindex – 1); • Quicksort(A, pivotindex + 1, right); • else • Insertionsort(A,left,right); • } Don’t use quicksort for small arrays. CUTOFF = 10 is reasonable.
From Reges/Stepp, page 708: assume that all complexity classes can process an input of size 100 in 100ms Examples of Each Complexity Class’s Growth Rate