L14 Quicksort and Performance Optimization Alice E. Fischer Fall 2018 Alice E. Fischer L4 Quicksort... 1/12 Fall 2018 1 / 12
Outline 1 The Quicksort Strategy 2 Diagrams 3 Code Alice E. Fischer L4 Quicksort... 2/12 Fall 2018 2 / 12
Outline Review and Overview Many sort algorithms have been invented but only a few are important. They are given here in order of general efficiency, slowest to fastest. Insertion sort, (O(n 2 )) on both a linked list and an array is used to sort small numbers of items. Mergesort (guaranteed O(n log 2 (n))) works best on linked lists or as an external sort. Quicksort ( generally O(n log 2 (n))) but must be implemented well, not badly. Radix sort ( O(n k), where k is the number of fields in the data being sorted). This has a substantial setup time and is used only for very large data sets. Alice E. Fischer L4 Quicksort... 3/12 Fall 2018 3 / 12
The Quicksort Strategy Step 1. Choosing a pivot value. The pivot value defines which other data items are small and which are large. We want a pivot value that is NOT the largest or smallest data in the array. Find one by comparing the first, middle, and last values in the array. Make the median of these 3 the pivot, and put it at the beginning of the array. Put the biggest of 3 at the right end, and the smallest in the middle. The pivot and the biggest of 3 will serve as sentinel values for comparison loops. We look at first-middle-last because that is reasonably fast and because it will work well in a common situation: the array starts with sorted data and ends with data that has been added to the sorted list. Alice E. Fischer L4 Quicksort... 4/12 Fall 2018 4 / 12
The Quicksort Strategy What is a sentinel? A sentinel value is a data value placed on one end of an array or list so that a loop can process the array without constantly checking whether the end of the array has been reached. This quicksort uses two scanning pointers, starting from the ends and moving toward each other. We put the pivot at the beginning because that will serve as a sentinel value to prevent our left-moving scans from falling off the left end of the array. We put the biggest at the right end because that will serve as a sentinel value to prevent our right-moving scans from falling off the right end of the array. Using sentinels cuts the number of comparisons in half and reduces the running time considerably. Alice E. Fischer L4 Quicksort... 5/12 Fall 2018 5 / 12
The Quicksort Strategy Step 2a: partitioning the data. Each recursive call specifies the beginning and the end of the part of the array to be sorted. Divide the unsorted data in this part of the array into two sections, things that are <= the pivot value and things that are >= the pivot. Equal data items may be in either section. Start a pointer L pointing at the pivot. Start a pointer R pointing at the last value in the array. L is trying to keep the little values and get rid of the big ones. It stops scanning when a big value is found. R is trying to keep the bit values and get rid of the little ones. It stops scanning when a little value is found. Then the values under L and R must be swapped, and the scan continues. Alice E. Fischer L4 Quicksort... 6/12 Fall 2018 6 / 12
The Quicksort Strategy Step 2b: partitioning the data. Leave the scanning loop when L and R meet or cross Swap the value under R with the pivot value. This puts the pivot somewhere in the middle of the array. The pivot value is now in its sorted position: to its left, values are smaller or equal, and to its right, values are greater or equal. Now sort the left and right sections, excluding the slot now occupied by the pivot. If the number of items in a section is smaller than the recursion cutoff, use insertion sort. Otherwise, make a recursive call on quicksort. An appropriate recursion cutoff for modern machines is between 20 and 30. This recursion terminates because each recursive call will sort at least one fewer element than the prior call. Alice E. Fischer L4 Quicksort... 7/12 Fall 2018 7 / 12
Diagrams Quicksort: Getting Started Sort 12 integers using a recursion cutoff of 3. Alice E. Fischer L4 Quicksort... 8/12 Fall 2018 8 / 12
Diagrams The first Recursive Call The first recursive call returns immediately because there are only 3 items in the small part of the array. The diagram shows progress of the second recursion: Alice E. Fischer L4 Quicksort... 9/12 Fall 2018 9 / 12
Diagrams Quicksort: The Last Recursive Call The data ends up in blocks of <=3 values, separated by sorted elements. Alice E. Fischer L4 Quicksort... 10/12 Fall 2018 10 / 12
Diagrams Quicksort: Finish up using Insertion Sort When the recursions are done, we sort the small blocks of values. Alice E. Fischer L4 Quicksort... 11/12 Fall 2018 11 / 12
Code Quicksort : Code quick.hpp Quick is a template class that lets you sort an array of any base type that supports the < and > operators. You can define these operators for any C++ class. main.cpp creates a Quick object and calls its quicksort function. Alice E. Fischer L4 Quicksort... 12/12 Fall 2018 12 / 12