2.3 Two classic sorting algorithms Critical components in the world’s computational infrastructure. • Full scientific understanding of their properties has enabled us to develop them into practical system sorts. • Quicksort honored as one of top 10 algorithms of 20th century in science and engineering.

‣ quicksort Mergesort. last lecture ‣ selection • Java sort for objects. ‣ duplicate keys • Perl, Python stable sort. ‣ system sorts Quicksort. this lecture • Java sort for primitive types. • C qsort, Unix, g++, Visual C++, Python.

th Algorithms in Java, 4 Edition · Robert Sedgewick and Kevin Wayne · Copyright © 2009 · January 22, 2010 4:05:04 PM 2

Quicksort

Basic plan. • Shuffle the array. • Partition so that, for some j - element a[j] is in place - no larger element to the left of j no smaller element to the right of j - Sir Charles Antony Richard Hoare • Sort each piece recursively. 1980 Turing Award ‣ quicksort

‣ selection input Q U I C K S O R T E X A M P L E ‣ duplicate keys shu!e K R A T E L E P U I M Q C X O S partitioning element

‣ system sorts partition E C A I E K L P U T M Q R X O S not greater not less sort left A C E E I K L P U T M Q R X O S sort right A C E E I K L M O P Q R S T U X result A C E E I K L M O P Q R S T U X Quicksort overview

3 4 Quicksort partitioning Quicksort: Java code for partitioning

Basic plan. private static int partition(Comparable[] a, int lo, int hi) • Scan i from left for an item that belongs on the right. { int i = lo, j = hi+1; • Scan j from right for item item that belongs on the left. while (true) Exchange a[i] and a[j]. { • while (less(a[++i], a[lo])) find item on left to swap • Continue until pointers cross. if (i == hi) break;

v a[i] while (less(a[lo], a[--j])) find item on right to swap i j 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (j == lo) break;

initial values -1 15 K R A T E L E P U I M Q C X O S if (i >= j) break; check if pointers cross scan left, scan right 1 12 K R A T E L E P U I M Q C X O S exch(a, i, j); swap exchange 1 12 K C A T E L E P U I M Q R X O S }

scan left, scan right 3 9 K C A T E L E P U I M Q R X O S exch(a, lo, j); swap with partitioning item exchange 3 9 K C A I E L E P U T M Q R X O S return j; return index of item now known tobefore be in vplace scan left, scan right 5 6 K C A I E L E P U T M Q R X O S } lo hi exchange 5 6 K C A I E E L P U T M Q R X O S before v during v Յ v Ն v scan left, scan right 6 5 K C A I E E L P U T M Q R X O S lo hi i j

!nal exchange 0 5 E C A I E K L P U T M Q R X O S after before v during v Յ v Ն v Յ v v Ն v result E C A I E K L P U T M Q R X O S lo hi i j lo j hi Partitioning trace (array contents before and after each exchange) during v Յ v Ն v after Յ v v Ն v Quicksort partitioning overview 5 6 i j lo j hi

after Յ v v Ն v Quicksort partitioning overview

lo j hi

Quicksort partitioning overview

Quicksort: Java implementation Quicksort trace

public class Quick lo j hi 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { initial values Q U I C K S O R T E X A M P L E private static int partition(Comparable[] a, int lo, int hi) random shu!e K R A T E L E P U I M Q C X O S { /* see previous slide */ } 0 5 15 E C A I E K L P U T M Q R X O S 0 3 4 E C A E I K L P U T M Q R X O S public static void sort(Comparable[] a) 0 2 2 A C E E I K L P U T M Q R X O S { 0 0 1 A C E E I K L P U T M Q R X O S StdRandom.shuffle(a); 1 1 A C E E I K L P U T M Q R X O S shuffle needed for sort(a, 0, a.length - 1); 4 4 A C E E I K L P U T M Q R X O S performance guarantee 6 6 15 A C E E I K L P U T M Q R X O S } no partition 7 9 15 A C E E I K L M O P T Q R X U S for subarrays 7 7 8 A C E E I K L M O P T Q R X U S private static void sort(Comparable[] a, int lo, int hi) of size 1 8 8 A C E E I K L M O P T Q R X U S { 10 13 15 A C E E I K L M O P S Q R T U X if (hi <= lo) return; 10 12 12 A C E E I K L M O P R Q S T U X int j = partition(a, lo, hi); 10 11 11 A C E E I K L M O P Q R S T U X sort(a, lo, j-1); 10 10 A C E E I K L M O P Q R S T U X sort(a, j+1, hi); 14 14 15 A C E E I K L M O P Q R S T U X } 15 15 A C E E I K L M O P Q R S T U X } result A C E E I K L M O P Q R S T U X

Quicksort trace (array contents after each partition)

7 8 Quicksort animation Quicksort: implementation details

50 random elements Partitioning in-place. Using a spare array makes partitioning easier (and stable), but is not worth the cost.

Terminating the loop. Testing whether the pointers cross is a bit trickier than it might seem.

Staying in bounds. The (j == lo) test is redundant (why?), but the (i == hi) test is not.

Preserving randomness. Shuffling is needed for performance guarantee.

Equal keys. When duplicates are present, it is (counter-intuitively) best algorithm position to stop on elements equal to the partitioning element. in order current subarray not in order http://www.sorting-algorithms.com/quick-sort

9 10

Quicksort: empirical analysis Quicksort: best case analysis

Running time estimates: Best case. Number of compares is ~ N lg N. • Home pc executes 108 compares/second. • Supercomputer executes 1012 compares/second.

(N2) mergesort (N log N) quicksort (N log N) computer thousand million billion thousand million billion thousand million billion

home instant 2.8 hours 317 years instant 1 second 18 min instant 0.3 sec 6 min

super instant 1 second 1 week instant instant instant instant instant instant

Lesson 1. Good algorithms are better than supercomputers. Lesson 2. Great algorithms are better than good ones.

11 12 Quicksort: worst case analysis Quicksort: average-case analysis

2 Worst case. Number of compares is ~ N / 2. Proposition I. The average number of compares CN to quicksort an array of N

elements is ~ 2N ln N (and the number of exchanges is ~ ⅓ N ln N).

Pf. CN satisfies the recurrence C0 = C1 = 0 and for N ! 2:

C0 + C1 + ... + CN 1 CN 1 + CN 2 + ... + C0 C =(N + 1) + − + − − N N N

partitioning left right partitioning probability • Multiply both sides by N and collect terms:

NCN = N(N + 1) + 2(C0 + C1 + ... + CN 1) − • Subtract this from the same equation for N-1:

NCN (N 1)CN 1 =2N +2CN 1 − − − − • Rearrange terms and divide by N(N+1):

CN CN 1 2 = − + N +1 N N +1

13 14

Quicksort: average-case analysis Quicksort: summary of performance characteristics

• Repeatedly apply above equation: Worst case. Number of compares is quadratic. N + (N-1) + (N-2) + … + 1 ~ N2 / 2. CN CN 1 2 • = − + N +1 N N +1 • More likely that your computer is struck by lightning. CN 2 2 2 = − + + previous equation N 1 N N +1 − Average case. Number of compares is ~ 1.39 N lg N. CN 3 2 2 2 = − + + + N 2 N 1 N N +1 • 39% more compares than mergesort. 2 − 2 2 − 2 = + + + ... + • But faster than mergesort in practice because of less data movement. 1 2 3 N +1

• Approximate sum by an integral: Random shuffle. Probabilistic guarantee against worst case. 1 1 1 • C 2(N + 1) 1+ + + ... N ∼ 2 3 N • Basis for math model that can be validated with experiments. ￿ ￿ N 1 2(N + 1) dx ∼ 1 x Caveat emptor. Many textbook implementations go quadratic if input: ￿ • Is sorted or reverse sorted. • Finally, the desired result: • Has many duplicates (even if randomized!) [stay tuned] C 2(N + 1) ln N 1.39N lg N N ∼ ≈ 15 16 Quicksort: practical improvements Quicksort with cutoff to insertion sort: visualization

input Median of sample. partitioning element result of • Best choice of pivot element = median. "rst partition • Estimate true median by taking median of sample.

Insertion sort small subarrays. • Even quicksort has too much overhead for tiny subarrays. • Can delay insertion sort until end.

~ 12/7 N ln N compares left subarray partially sorted Optimize parameters. ~ 12/35 N ln N exchanges • Median-of-3 random elements. • Cutoff to insertion sort for " 10 elements.

Non-recursive version. guarantees O(log N) stack size Use explicit stack. • both subarrays • Always sort smaller half first. partially sorted result

17 Quicksort with median-of-3 partitioning and cuto! for small subarrays 18

Selection

Goal. Find the kth largest element. Ex. Min (k = 0), max (k = N-1), median (k = N/2).

Applications. • Order statistics. • Find the “top k.”

‣ quicksort Use theory as a guide. ‣ selection • Easy O(N log N) upper bound. ‣ duplicate keys • Easy O(N) upper bound for k = 1, 2, 3. ‣ system sorts • Easy #(N) lower bound.

Which is true? • #(N log N) lower bound? is selection as hard as sorting? • O(N) upper bound? is there a linear-time algorithm for all k?

19 20 Quick-select Quick-select: mathematical analysis

Partition array so that: Proposition. Quick-select takes linear time on average. • Element a[j] is in place. Pf sketch. • No larger element to the left of j. • Intuitively, each partitioning step roughly splits array in half: • No smaller element to the right of j. N + N/2 + N/4 + … + 1 ~ 2N compares. • Formal analysis similar to quicksort analysis yields: Repeat in one subarray, depending on j; finished when j equals k. CN = 2 N + k ln ( N / k) + (N - k) ln (N / (N - k))

before v public static Comparable select(Comparable[] a, int k) { lo hi if a[k] is here if a[k] is here StdRandom.shuffle(a); set hi to j-1 set lo to j+1 Ex. (2 + 2 ln 2) N compares to find the median. int lo = 0, hi = a.length - 1; during v Յ v Ն v while (hi > lo) i j { int j = partition(a, lo, hi); after Յ v v Ն v if (j < k) lo = j + 1; else if (j > k) hi = j - 1; lo j hi else return a[k]; 2 Quicksort partitioning overview Remark. Quick-select uses ~ N /2 compares in worst case, } return a[k]; but as with quicksort, the random shuffle provides a probabilistic guarantee. }

21 22

Theoretical context for selection Generic methods

Challenge. Design algorithm whose worst-case running time is linear. In our select() implementation, client needs a cast.

Proposition. [Blum, Floyd, Pratt, Rivest, Tarjan, 1973] There exists a Double[] a = new Double[N]; compare-based whose worst-case running time is linear. for (int i = 0; i < N; i++) a[i] = StdRandom.uniform(); unsafe cast Double median = (Double) Quick.select(a, N/2); Remark. But, algorithm is too complicated to be useful in practice. required

The compiler also complains.

Use theory as a guide. % javac Quick.java • Still worthwhile to seek practical linear-time (worst-case) algorithm. Note: Quick.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. • Until one is discovered, use quick-select if you don’t need a full sort.

Q. How to fix?

23 24 Generic methods

Pedantic (safe) version. Compiles cleanly, no cast needed in client.

public class QuickPedantic generic type variable { (value inferred from argument a[]) public static > Key select(Key[] a, int k) { /* as before */ }

return type matches array type public static > void sort(Key[] a) { /* as before */ }

private static > int partition(Key[] a, int lo, int hi) { /* as before */ } ‣ quicksort ‣ private static > boolean less(Key v, Key w) selection { /* as before */ } ‣ duplicate keys

private static > void exch(Key[] a, int i, int j) ‣ system sorts { Key swap = a[i]; a[i] = a[j]; a[j] = swap; }

} can declare variables of generic type

http://www.cs.princeton.edu/algs4/35applications/QuickPedantic.java.html

Remark. Obnoxious code needed in system sort; not in this course (for brevity).

25 26

Duplicate keys Duplicate keys

Often, purpose of sort is to bring records with duplicate keys together. Mergesort with duplicate keys. Always ~ N lg N compares. • Sort population by age. • Find collinear points. see Assignment 3 Quicksort with duplicate keys. • Remove duplicates from mailing list. • Algorithm goes quadratic unless partitioning stops on equal keys! • Sort job applicants by college attended. • 1990s C user found this defect in qsort(). sorted by time sorted by city (unstable) sorted by city (stable) Chicago 09:00:00 Chicago 09:25:52 Chicago 09:00:00 Phoenix 09:00:03 Chicago 09:03:13 Chicago 09:00:59 several textbook and system implementations Typical characteristics of such applications.Houston 09:00:13 Chicago 09:21:05 Chicago 09:03:13 also have this defect Chicago 09:00:59 Chicago 09:19:46 Chicago 09:19:32 • Huge array. Houston 09:01:10 Chicago 09:19:32 Chicago 09:19:46 Chicago 09:03:13 Chicago 09:00:00 Chicago 09:21:05 • Small number of key values. Seattle 09:10:11 Chicago 09:35:21 Chicago 09:25:52 Seattle 09:10:25 Chicago 09:00:59 Chicago 09:35:21 Phoenix 09:14:25 Houston 09:01:10 Houston 09:00:13 NOT S T O P O N E Q U A L K E Y S Chicago 09:19:32 Houston 09:00:13 Houston 09:01:10 sorted Chicago 09:19:46 Phoenix 09:37:44 sorted Phoenix 09:00:03 Chicago 09:21:05 Phoenix 09:00:03 Phoenix 09:14:25 Seattle 09:22:43 Phoenix 09:14:25 Phoenix 09:37:44 swap if we don't stop if we stop on Seattle 09:22:54 Seattle 09:10:25 Seattle 09:10:11 on equal keys equal keys Chicago 09:25:52 Seattle 09:36:14 Seattle 09:10:25 Chicago 09:35:21 Seattle 09:22:43 Seattle 09:22:43 Seattle 09:36:14 Seattle 09:10:11 Seattle 09:22:54 Phoenix 09:37:44 Seattle 09:22:54 Seattle 09:36:14

Stability when sorting on a second key

key

27 28 Duplicate keys: the problem 3-way partitioning

Mistake. Put all keys equal to the partitioning element on one side. Goal. Partition array into 3 parts so that: Consequence. ~ N2 / 2 compares when all keys equal. • Elements between lt and gt equal to partition element v. No larger elements to left of lt. B A A B A B B B C C C A A A A A A A A A A A • • No smaller elements to right of gt.

before v Recommended. Stop scans on keys equal to the partitioning element. lo hi Consequence. ~ N lg N compares when all keys equal. during v

B A A B A B C C B C B A A A A A A A A A A A lt i gt after v

lo lt gt hi

3-way partitioning

Desirable. Put all keys equal to the partitioning element in place. Dutch national flag problem. [Edsger Dijkstra] • Conventional wisdom until mid 1990s: not worth doing. A A A B B B B B C C C A A A A A A A A A A A • New approach discovered when fixing mistake in C library qsort(). • Now incorporated into qsort() and Java system sort.

29 30

3-way partitioning: Dijkstra's solution 3-way partitioning: trace

3-way partitioning. • Let v be partitioning element a[lo]. a[] • Scan i from left to right. v lt i gt 0 1 2 3 4 5 6 7 8 9 10 11 a[i] less than v: exchange a[lt] with a[i] and increment both lt and i - 0 0 11 R B W W R W B R R W B R - a[i] greater than v: exchange a[gt] with a[i] and decrement gt 0 1 11 R B W W R W B R R W B R - a[i] equal to v: increment i 1 2 11 B R W W R W B R R W B R 1 2 10 B R R W R W B R R W B W before v 1 3 10 B R R W R W B R R W B W 1 3 9 B R R B R W B R R W W W lo hi All the right properties. 2 4 9 B B R R R W B R R W W W during v • In-place. 2 5 9 B B R R R W B R R W W W lt i gt 2 5 8 B B R R R W B R R W W W Not much code. • after v 2 5 7 B B R R R R B R W W W W • Small overhead if no equal keys. 2 6 7 B B R R R R B R W W W W lo lt gt hi 3 7 7 B B B R R R R R W W W W 3-way partitioning 3 8 7 B B B R R R R R W W W W

3-way partitioning trace (array contents after each loop iteration)

31 32 3-way quicksort: Java implementation 3-way quicksort: visual trace

private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int lt = lo, gt = hi; Comparable v = a[lo]; int i = lo; while (i <= gt) { int cmp = a[i].compareTo(v); equal to partitioning element if (cmp < 0) exch(a, lt++, i++); else if (cmp > 0) exch(a, i, gt--); else i++; }

before v sort(a, lo, lt - 1); lo hi sort(a, gt + 1, hi); } during v

lt i gt Visual trace of quicksort with 3-way partitioning after v

lo lt gt hi

3-way partitioning

33 34

Duplicate keys: lower bound

Sorting lower bound. If there are n distinct keys and the ith one occurs

xi times, any compare-based must use at least

n N! xi lg x lg N lg N when all distinct; x ! x ! x ! ∼− i N 1 2 n i=1 linear when only a constant number of distinct keys ￿ ··· ￿ ￿ compares in the worst case.

‣ selection Proposition. [Sedgewick-Bentley, 1997] proportional to lower bound ‣ duplicate keys Quicksort with 3-way partitioning is entropy-optimal. ‣ comparators Pf. [beyond scope of course] ‣ system sorts

Bottom line. Randomized quicksort with 3-way partitioning reduces running time from linearithmic to linear in broad class of applications.

35 36 Sorting applications Java system sorts

Sorting algorithms are essential in a broad variety of applications: Java uses both mergesort and quicksort. • Sort a list of names. • Arrays.sort() sorts array of Comparable or any primitive type. • Organize an MP3 library. • Uses quicksort for primitive types; mergesort for objects. • Display Google PageRank results. obvious applications • List RSS news items in reverse chronological order. import java.util.Arrays;

• Find the median. public class StringSort • Find the closest pair. { public static void main(String[] args) Binary search in a database. • problems become easy once items { • Identify statistical outliers. are in sorted order String[] a = StdIn.readAll().split("\\s+"); Find duplicates in a mailing list. Arrays.sort(a); • for (int i = 0; i < N; i++) StdOut.println(a[i]); • Data compression. } } • Computer graphics. • Computational biology. non-obvious applications • Supply chain management. Load balancing on a parallel computer. • Q. Why use different algorithms, depending on type? . . . Every system needs (and has) a system sort! 37 38

Java system sort for primitive types Achilles heel in Bentley-McIlroy implementation (Java system sort)

Engineering a sort function. [Bentley-McIlroy, 1993] Based on all this research, Java’s system sort is solid, right? • Original motivation: improve qsort(). more disastrous consequences in C • Basic algorithm = 3-way quicksort with cutoff to insertion sort. A killer input. • Partition on Tukey's ninther: median of the medians of 3 samples, • Blows function call stack in Java and crashes program. each of 3 elements. Would take quadratic time if it didn’t crash first. approximate median-of-9 •

nine evenly % more 250000.txt % java IntegerSort < 250000.txt R L A P M C G A X Z K R B R J J E spaced elements 0 Exception in thread "main" 218750 java.lang.StackOverflowError groups of 3 R A M G X K B J E 222662 at java.util.Arrays.sort1(Arrays.java:562) medians M K E 11 at java.util.Arrays.sort1(Arrays.java:606) 166672 at java.util.Arrays.sort1(Arrays.java:608) ninther K 247070 at java.util.Arrays.sort1(Arrays.java:608) 83339 at java.util.Arrays.sort1(Arrays.java:608) ......

Why use Tukey's ninther? 250,000 integers Java's sorting library crashes, even if • Better partitioning than random shuffle. between 0 and 250,000 you give it as much stack space as Windows allows • Less costly than random shuffle.

39 40 Achilles heel in Bentley-McIlroy implementation (Java system sort) System sort: Which algorithm to use?

McIlroy's devious idea. [A Killer Adversary for Quicksort] Many sorting algorithms to choose from: • Construct malicious input while running system quicksort, in response to elements compared. Internal sorts. • If v is partitioning element, commit to (v < a[i]) and (v < a[j]), but don't • Insertion sort, selection sort, bubblesort, shaker sort. commit to (a[i] < a[j]) or (a[j] > a[i]) until a[i] and a[j] are compared. • Quicksort, mergesort, , samplesort, . • Solitaire sort, red-black sort, splaysort, Dobosiewicz sort, psort, ... Consequences. • Confirms theoretical possibility. External sorts. Poly-phase mergesort, cascade-merge, oscillating sort. • Algorithmic complexity attack: you enter linear amount of data; server performs quadratic amount of work. Radix sorts. Distribution, MSD, LSD, 3-way radix quicksort.

Remark. Attack is not effective if array is shuffled before sort. Parallel sorts. • Bitonic sort, Batcher even-odd sort. • Smooth sort, cube sort, column sort. • GPUsort. Q. Why do you think system sort is deterministic?

41 42

System sort: Which algorithm to use? Sorting summary

Applications have diverse attributes. • Stable? • Parallel? inplace? stable? worst average best remarks • Deterministic? selection x N 2 / 2 N 2 / 2 N 2 / 2 N exchanges • Keys all distinct? • Multiple key types? insertion x x N 2 / 2 N 2 / 4 N use for small N or partially ordered • Linked list or arrays? shell x ? ? N tight code, subquadratic • Large or small records? N log N probabilistic guarantee Is your array randomly ordered? quick x N 2 / 2 2 N ln N N lg N • fastest in practice Need guaranteed performance? many more combinations of • improves quicksort in presence of attributes than algorithms 3-way quick x N 2 / 2 2 N ln N N duplicate keys

Elementary sort may be method of choice for some combination. merge x N lg N N lg N N lg N N log N guarantee, stable Cannot cover all combinations of attributes. ??? x x N lg N N lg N N lg N holy sorting grail

Q. Is the system sort good enough? A. Usually.

43 44 Which sorting algorithm?

lifo find data data data data hash data fifo fifo fifo fifo exch fifo fifo exch data data find find fifo lifo data fifo type exch hash hash find type link find hash hash heap heap hash hash leaf hash heap heap lifo lifo heap heap heap heap sort less link link leaf link exch leaf link left list list left sort node left list leaf push push less find lifo less push lifo root root lifo list left lifo find push sort sort link push find link root root type type list root path list leaf list leaf leaf sort leaf list next tree tree left tree tree null next node null null node null null path less null path path null path path tree root path node node path node node exch sink push left link tree left type left swim root less sort exch less root less null sink exch type less exch push node sort sort sink sink next sink sink next type swap swim swim sink swim swim sink tree swim next next swap next next swap push tree swap swap swim swap swap swim swap type

original ? ? ? ? ? ? sorted

45