Professional Documents
Culture Documents
2 Mergesort
Two classic sorting algorithms Critical components in the worlds 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.
today
next lecture
Mergesort Basic plan. Divide array into two halves. Recursively sort each half. Merge two halves.
M E E A
E E E E
R G G E
G M M E
E O O E
S R R G
O R R L
R S S M
T T A M
E E E O
X X E P
A A L R
M M M R
P P P S
L L T T
E E X X
Mergesort overview
Merging Q. How to combine two sorted subarrays into a sorted whole. A. Use an auxiliary array.
a[] k
input copy
aux[] 6 C C 7 E E 8 R R 9 T T 0 5 6 7 7 7 8 8 8 8 9 E E E E E E E G G G G G G M M M M M M M R R R R R R R R A 0 i j 0 E 1 E 2 G 3 M 4 R 5 A
copy
0 E E
1 E E
2 G G
3 M M
4 R R
5 A A
0 1 2 3 4 5 6 7 8 9
merged result
A A A A A A A A A A A C C C C C C C C C C E E E E E E E E E E E E E E E E E E E E E E E E G G G G G G M M M M M R R R R R R R T T
0 1 2 2 3 4 5 5
int i = lo, j = mid+1; for (int k = lo; k <= hi; k++) { if (i > mid) else if (j > hi) else if (less(aux[j], aux[i])) else } assert isSorted(a, lo, hi); }
= = = =
merge
6 10
A A
G G
L H
O I
R L
H M
I
k
Assertions Assertion. Statement to test assumptions about your program. Helps detect logic bugs. Documents code.
public class Merge { private static Comparable[] aux; private static void merge(Comparable[] a, int lo, int mid, int hi) { /* as before */ } private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int mid = lo + (hi - lo) / 2; sort(a, lo, mid); sort(a, mid+1, hi); merge(a, lo, m, hi); } public static void sort(Comparable[] a) { aux = new Comparable[a.length]; sort(a, 0, a.length - 1); } }
lo mid hi
Best practices. Use to check internal invariants. Assume assertions will be disabled in production code (e.g., don't use for external argument-checking).
7
10
11
12
13
14
15
16
17
18
19
Mergesort trace
Mergesort animation
50 random elements
lo
hi
merge(a, 0, 0, 1) merge(a, 2, 2, 3) merge(a, 0, 1, 3) merge(a, 4, 4, 5) merge(a, 6, 6, 7) merge(a, 4, 5, 7) merge(a, 0, 3, 7) merge(a, 8, 8, 9) merge(a, 10, 10, 11) merge(a, 8, 9, 11) merge(a, 12, 12, 13) merge(a, 14, 14, 15) merge(a, 12, 13, 15) merge(a, 8, 11, 15) merge(a, 0, 7, 15)
0 M E E E E E E E E E E E E E E A
1 E M M G G G G E E E E E E E E E
2 R R G M M M M G G G G G G G G E
3 G G R R R R R M M M M M M M M E
4 E E E E E E E O O O O O O O O E
5 S S S S S S O R R R R R R R R G
6 O O O O O O R R R R R R R R R L
a[] 7 8 R T R T R T R T R T R T S T S T S E S E S A S A S A S A S A M M
9 10 11 12 13 14 15 E X A M P L E E X A M P L E E X A M P L E E X A M P L E E X A M P L E E X A M P L E E X A M P L E E X A M P L E T X A M P L E T A X M P L E E T X M P L E E T X M P L E E T X M P E L E T X E L M P E E L M P T X O P R R S T X
not in order
10
Mergesort animation
50 reverse-sorted elements
Mergesort: empirical analysis Running time estimates: Home pc executes 108 comparisons/second. Supercomputer executes 1012 comparisons/second.
insertion sort (N2) computer home super thousand instant instant million 2.8 hours 1 second billion 317 years 1 week
mergesort (N log N) thousand instant instant million 1 second instant billion 18 min instant
Mergesort: mathematical analysis Proposition. Mergesort uses ~ 2 N lg N data moves to sort any array of size N. Def. D(N) = number of data moves to mergesort an array of size N. = D(N / 2) + D(N / 2) + 2 N
left half right half merge
Mergesort recurrence: proof 1 Mergesort recurrence. D(N) = 2 D(N / 2) + 2 N for N > 1, with D(1) = 0. Proposition. If N is a power of 2, then D(N) = 2 N lg N. Pf.
D(N)
2N
= 2N
Mergesort recurrence. D(N) = 2 D(N / 2) + 2 N for N > 1, with T(1) = 0. Not quite right for odd N. Similar recurrence holds for many divide-and-conquer algorithms.
lg N D(N/4)
D(N/2)
D(N/2)
2 (2N/2)
= 2N
D(N/4)
D(N/4)
D(N/4)
4 (2N/4) ...
= 2N
D(N / 2k)
2k (2N/2k) ...
= 2N
Solution. D(N) ~ 2 N lg N. For simplicity, we'll prove when N is a power of 2. True for all N. [see COS 340]
13
D(2)
D(2)
D(2)
D(2)
D(2)
D(2)
D(2)
D(2)
N/2 (4)
= 2N 2N lg N
14
Mergesort recurrence: proof 2 Mergesort recurrence. D(N) = 2 D(N / 2) + 2 N for N > 1, with D(1) = 0. Proposition. If N is a power of 2, then D(N) = 2 N lg N. Pf.
D(N) = 2 D(N/2) + 2N given divide both sides by N algebra apply to first term apply to first term again
Mergesort recurrence: proof 3 Mergesort recurrence. D(N) = 2 D(N / 2) + 2 N for N > 1, with D(1) = 0. Proposition. If N is a power of 2, then D(N) = 2 N lg N.
D(N) / N = 2 D(N/2) / N + 2 = D(N/2) / (N/2) + 2 = D(N/4) / (N/4) + 2 + 2 = D(N/8) / (N/8) + 2 + 2 + 2 ... = D(N/N) / (N/N) + 2 + 2 + ... + 2 = 2 lg N
Pf. [by induction on N] Base case: N = 1. Inductive hypothesis: D(N) = 2N lg N. Goal: show that D(2N) = 2(2N)lg (2N).
= 4 N lg (2N)
15
16
Mergesort: number of compares Proposition. Mergesort uses between ! N lg N and N lg N compares to sort any array of size N. Pf. The number of compares for the last merge is between ! N lg N and N.
Mergesort analysis: memory Proposition G. Mergesort uses extra space proportional to N. Pf. The array aux[] needs to be of size N for the last merge.
A A
C B
D C
G D
H E
I F
M G
N H
U I
V J
B M
E N
F O
J P
O Q
P R
Q S
R T
S U
T V
merged result
Def. A sorting algorithm is in-place if it uses O(log N) extra memory. Ex. Insertion sort, selection sort, shellsort. Challenge for the bored. In-place merge. [Kronrud, 1969]
17 18
3.2
Mergesort
235
Mergesort: practical improvements Use insertion sort for small subarrays. Mergesort has too much overhead for tiny subarrays. Cutoff to insertion sort for ! 7 elements.
Mergesort visualization
rst subarray
second subarray
rst merge
Stop if already sorted. Is biggest element in first half " smallest element in second half? Helps for partially-ordered arrays.
A A B B C C D D E E F F G G H H I I J J M M N N O O P P Q Q R R S S T T U U V V
rst half sorted
Eliminate the copy to the auxiliary array. Save time (but not space) by switching the role of the input and auxiliary array in each recursive call.
second half sorted
result
Visual trace of top-down mergesort for with cuto for small subarrays
20
private static Comparable[] aux; // See page 230 for merge() code.
public static void sort(Comparable[] a) { // Do lg N passes of pairwise merges. int N = a.length; aux = new Comparable[N]; for (int sz = 1; sz < N; sz = sz+sz) // sz: subarray size Bottom-up mergesort lo = 0; lo < N-sz; lo += sz+sz) // lo: subarray index for (int merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1)); } }
Basic plan.
Bottom-up mergesort consists of a sequence of passes of size 1. Pass through array, merging subarrays over the whole array, doing m-by-m merges,
Repeat size is an even multiple ofsz (otherwise4, is less than....). When N is a power of two, as in our array for subarrays of size 2, it 8, 16, sz
example, the merges performed are the same as for top-down mergesort, in a different order.
0 M E E E E E E E E E E E E E E A 1 E M M M M M M M M G G G G E E E 2 R R G G G G G G G M M M M G G E 3 G G R R R R R R R R R R R M M E 4 E E E E E E E E E E E E E O O E 5 S S S S S S S S S S O O O R R G 6 O O O O O O O O O O R R R R R L a[i] 7 8 R T R T R T R T R T R E R E R E R E R S S S S S M E E A A A A M 9 10 11 12 13 14 15 E X A M P L E E X A M P L E E X A M P L E E X A M P L E E X A M P L E T X A M P L E T A X M P L E T A X M P L E T A X M P E L T T E E E E O A A T T T E P X X X X X L R M M M E E M R P P P L L P S E E E M M T T L L L P P X X
starting with sz equal to 1 and doubling sz on each pass. The nal subarray is of size m only when the
merge(a, 0, 0, 1) merge(a, 2, 2, 3) merge(a, 4, 4, 5) merge(a, 6, 6, 7) merge(a, 8, 8, 9) merge(a, 10, 10, 11) merge(a, 12, 12, 13) merge(a, 14, 14, 15)
sz = 4
sz = 2
merge(a, 0, 1, 3) merge(a, 4, 5, 7) merge(a, 8, 9, 11) merge(a, 12, 13, 15) 0, 3, 7) 8, 11, 15) 0, 7, 15)
sz = 8
merge(a, merge(a,
merge(a,
sz = 16
public class MergeBU { private static Comparable[] aux; private static void merge(Comparable[] a, int lo, int mid, int hi) { /* as before */ } public { int aux for static void sort(Comparable[] a) N = a.length; = new Comparable[N]; (int sz = 1; sz < N; sz = sz+sz) for (int lo = 0; lo < N-sz; lo += sz+sz) merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
16
} }
32
Complexity of sorting Computational complexity. Framework to study efficiency of algorithms for solving a particular problem X. Machine model. Focus on fundamental operations. Upper bound. Cost guarantee provided by some algorithm for X. Lower bound. Proven limit on cost guarantee of all algorithms for X. Optimal algorithm. Algorithm with best cost guarantee for X.
25
Compare-based lower bound for sorting Proposition. Any compare-based sorting algorithm must use at least
a < b yes no
code between compares (e.g., sequence of exchanges) height of tree = worst-case number of compares
lg N ! ~ N lg N compares in the worst-case. Pf. Assume input consists of N distinct values a1 through aN. Worst case dictated by height h of decision tree. Binary tree of height h has at most 2 h leaves. N ! different orderings ! at least N ! leaves.
b < c yes
a < c no yes no
a b c
a < c
b a c
b < c
yes
no
yes
no
h
a c b c a b b c a c b a
at least N! leaves
27
Compare-based lower bound for sorting Proposition. Any compare-based sorting algorithm must use at least lg N ! ~ N lg N compares in the worst-case. Pf. Assume input consists of N distinct values a1 through aN. Worst case dictated by height h of decision tree. Binary tree of height h has at most 2 h leaves. N ! different orderings ! at least N ! leaves.
Complexity of sorting Machine model. Focus on fundamental operations. Upper bound. Cost guarantee provided by some algorithm for X. Lower bound. Proven limit on cost guarantee of all algorithms for X. Optimal algorithm. Algorithm with best cost guarantee for X. Example: sorting. Machine model = # compares. Upper bound = ~ N lg N from mergesort. Lower bound = ~ N lg N. Optimal algorithm = mergesort.
! h " lg N ! ~ N lg N
Stirling's formula
29
30
Complexity results in context Other operations? Mergesort optimality is only about number of compares. Space? Mergesort is not optimal with respect to space usage. Insertion sort, selection sort, and shellsort are space-optimal.
Complexity results in context (continued) Lower bound may not hold if the algorithm has information about: The initial order of the input. The distribution of key values. The representation of the keys.
Partially-ordered arrays. Depending on the initial order of the input, we may not need N lg N compares.
insertion sort requires only N-1 compares on an already sorted array
Duplicate keys. Depending on the input distribution of duplicates, we may not need N lg N compares. Lessons. Use theory as a guide. Ex. Don't try to design sorting algorithm that uses ! N lg N compares. Digital properties of keys. We can use digit/character compares instead of key compares for numbers and strings.
stay tuned for radix sorts stay tuned for 3-way quicksort
31
32
33
34
natural order
Generalized compare Comparable interface: sort uses types natural order. Problem 1. May want to use a non-natural order. Problem 2. Desired data type may not come with a natural order. Ex. Sort strings by: Natural order. Case insensitive. Spanish. British phone book.
Now is the time is Now the time caf cafetero cuarto churro nube oo McKinley Mackintosh
pre-1994 order for digraphs ch and ll and rr
37
definition of what it means to compare two objects of that type. Can add any number of new orders to a data type. Can add an order to a library data type with no natural order.
import java.text.Collator;
38
Sort implementation with comparators To support comparators in our sort implementations: Pass Comparator to sort() and less(). Use it in less().
public class ReverseOrder implements Comparator<String> { public int compare(String a, String b) { return b.compareTo(a); } }
comparator implementation
39
40
Generalized compare Comparators enable multiple sorts of a single array (by different keys). Ex. Sort students by name or by section.
Arrays.sort(students, Student.BY_NAME); Arrays.sort(students, Student.BY_SECT);
public class Student { public static final Comparator<Student> BY_NAME = new ByName(); public static final Comparator<Student> BY_SECT = new BySect(); private final String name; private final int section; ... private static class ByName implements Comparator<Student> { public int compare(Student a, Student b) { return a.name.compareTo(b.name); } } private static class BySect implements Comparator<Student> { public int compare(Student a, Student b) { return a.section - b.section; } } }
only use this trick if no danger of overflow
sort by name
sort by section
3 4 2 1 3 4 3 3
A C A A A B B A
097 Little 121 Whitman 308 Blair 11 Dickinson 101 Brown 22 Brown 22 Brown 343 Forbes
1 2 3 3 3 3 4 4
A A A A B A C B
11 Dickinson 308 Blair 097 Little 101 Brown 22 Brown 343 Forbes 121 Whitman 22 Brown
41
42
Generalized compare problem A typical application. First, sort by name; then sort by section.
Sorting challenge 5 Q. Which sorts are stable? Insertion sort? Selection sort? Shellsort? Mergesort?
Arrays.sort(students, Student.BY_NAME);
Arrays.sort(students, Student.BY_SECT);
sorted by time sorted by location (not stable) sorted by location (stable)
3 4 2 1 3 4 3 3
A C A A A B B A
097 Little 121 Whitman 308 Blair 11 Dickinson 101 Brown 22 Brown 22 Brown 343 Forbes
1 2 3 3 3 3 4 4
A A B A A A C B
11 Dickinson 308 Blair 22 Brown 097 Little 101 Brown 343 Forbes 121 Whitman 22 Brown
@#%&@!!. Students in section 3 no longer in order by name. A stable sort preserves the relative order of records with equal keys.
43
Chicago Phoenix Houston Chicago Houston Chicago Seattle Seattle Phoenix Chicago Chicago Chicago Seattle Seattle Chicago Chicago Seattle Phoenix
09:00:00 09:00:03 09:00:13 09:00:59 09:01:10 09:03:13 09:10:11 09:10:25 09:14:25 09:19:32 09:19:46 09:21:05 09:22:43 09:22:54 09:25:52 09:35:21 09:36:14 09:37:44
Chicago Chicago Chicago Chicago Chicago Chicago Chicago Chicago Houston Houston Phoenix Phoenix Phoenix Seattle Seattle Seattle Seattle Seattle
09:25:52 09:03:13 09:21:05 09:19:46 09:19:32 09:00:00 09:35:21 09:00:59 09:01:10 09:00:13 09:37:44 09:00:03 09:14:25 09:10:25 09:36:14 09:22:43 09:10:11 09:22:54
Chicago Chicago Chicago Chicago Chicago Chicago Chicago Chicago Houston Houston Phoenix Phoenix Phoenix Seattle Seattle Seattle Seattle Seattle
09:00:00 09:00:59 09:03:13 09:19:32 09:19:46 09:21:05 09:25:52 09:35:21 09:00:13 09:01:10 09:00:03 09:14:25 09:37:44 09:10:11 09:10:25 09:22:43 09:22:54 09:36:14
44
i 0 1 2
min 2 1 2
0 B1 A A A
1 B2 B2 B2 B2
2 A B1 B1 B1
A. No, long-distance exchange might move left element to the right of some equal element.
47
lo
hi
merge(a, 0, 0, 1) merge(a, 2, 2, 3) merge(a, 4, 4, 5) merge(a, 6, 6, 7) merge(a, 8, 8, 9) merge(a, 10, 10, 11) merge(a, 12, 12, 13) merge(a, 14, 14, 15) merge(a, 0, 1, 3) merge(a, 4, 5, 7) merge(a, 8, 9, 11) merge(a, 12, 13, 15) merge(a, 0, 3, 7) merge(a, 8, 11, 15) merge(a, 0, 7, 15)
0 M E E E E E E E E E E E E E E A
1 E M M G G E E E E G G E E E E E
2 R R G M M G G G G M M G G G G E
3 G G R R R M M M M R R M M M M E
4 E E E E E O O O O E E O O O O E
5 S S S S S R R R R S O R R R R G
6 O O O O O R R R R O R R R R R L
a[i] 7 8 R T R T R T R T R T S E S E S A S A R T S T S A S A S T S A M M
9 10 11 12 13 14 15 E X A M P L E E X A M P L E E X A M P L E E X A M P L E E X A M P L E T X A M P L E T A X M P L E E T X M P L E E T X M P E L E X A M P L E E X A M P L E E T X M P L E E T X E L M P E X A M P L E E E L M P T X O P R R S T X
Sorting challenge 5 (summary) Q. Which sorts are stable ? Yes. Insertion sort, mergesort. No. Selection sort, shellsort.
private static void merge(Comparable[] a, int lo, int mid, int hi) { for (int k = lo; k <= hi; k++) aux[k] = a[k]; int i = lo, j = mid+1; for (int k = lo; k <= hi; k++) { if (i > mid) else if (j > hi) else if (less(aux[j], aux[i])) else } }
Note. Need to carefully check code (less than vs less than or equal).
a[k] a[k] a[k] a[k] = = = = aux[j++]; aux[i++]; aux[j++]; aux[i++];
Postscript: optimizing mergesort (a short history) Goal. Remove instructions from the inner loop.
private static void merge(Comparable[] a, int lo, int mid, int hi) { for (int k = lo; k <= hi; k++) aux[k] = a[k]; int i = lo, j = mid+1; for (int k = lo; k <= hi; k++) if (i > mid) else if (j > hi ) else if (less(aux[j], aux[i])) else }
a[M] := maxint; b[N] := maxint; for (int i = 0, j = 0, k = 0; k < M+1; k++) if (less(aux[j], aux[i])) aux[k] = a[i++]; aux[k] = b[j++];
= = = =
0 a[]
j b[]
!
I L
H
k
aux[]
lo aux[]
mid
hi
A A
G G
L H
O I
R L
H M
I
k
Problem 1. Still need copy. Problem 2. No good place to put sentinels. Problem 3. Complicates data-type interface (what is infinity for your type?)
53 54
a[]
Postscript: Optimizing mergesort (a short history) Idea 3 (1990s). Eliminate copy with recursive argument switch.
int mid = (lo+hi)/2; mergesortABr(b, a, lo, mid); mergesortABr(b, a, mid+1, r); mergeAB(a, lo, b, lo, mid, b, mid+1, hi);
lo aux[]
mid
hi
A A
G G
L H
O I
R L
T M
S
k
a[]
Arrays.sort()
Sorting challenge 6 Problem. Choose mergesort for Algs 4th edition. Recursive argument switch is out (recommended only for pros). Q. Why not use reverse array copy?
private static void merge(Comparable[] a, int lo, int mid, int hi) { for (int i = lo; i <= mid; i++) aux[i] = a[i]; for (int j = mid+1; j <= hi; j++) aux[j] = a[hi-j+mid+1]; int i = lo, j = hi; for (int k = lo; k <= hi; k++) if (less(aux[j], aux[i])) a[k] = aux[j--]; else a[k] = aux[i++]; }
57