A collection is a data type that stores groups of items
data type
key operations
data structure
stack
push, pop
LL, resizing array
queue
enqueue, dequeue
LL, resizing array
priority queue
insert, delete-max
binary heap
symbol table
put, get, delete
BST, hash table
set
add, contains, delete
BST, hash table
collections
“Fred Brooks, 2017.04.05
Show me your code and conceal your data structures, and I shall continue to be mystified. Show me your data structures, and I won't usually need your code; it'll be obvious.
—Fred Brooks
”
priority queue
Collections: Insert and delete items. Which item to delete?
Challenge: Find the largest \(M\) items in a stream of \(N\) items, where \(N\) is huge and \(M\) is large
fraud detection: isolate large dollar transactions
NSA monitoring: flag most suspicious documents
Constraint: can store some, but not enough memory to store \(N\) items (can only look at everything once)
priority queue: client example
// Transaction data type is Comparable (ordered by $)
// use a min-oriented priority queue
MinPQ<Transaction> pq = new MinPQ<Transaction>();
while(StdIn.hasNextLine()) {
String line = StdIn.readLine();
Transaction transaction = new Transaction(line);
pq.insert(transaction);
if(pq.size() > M)
pq.delMin(); // pq now contains largest M items
}
pq: unordered and ordered array
// unordered ordered reverse
// op ret sz contents contents ordered
insert(P) 1 P P P
insert(Q) 2 P Q P Q Q P
insert(E) 3 P Q E E P Q Q P E
delMin() E 2 P Q P Q Q P
insert(X) 3 P Q X P Q X X Q P
insert(A) 4 P Q X A A P Q X X Q P A
insert(M) 5 P Q X A M A M P Q X X Q P M A
delMin() A 4 P Q X M M P Q X X Q P M
insert(P) 5 P Q X M P M P P Q X X Q P P M
insert(L) 6 P Q X M P L L M P P Q X X Q P P M L
insert(E) 7 P Q X M P L E E L M P P Q X X Q P P M L E
delMin() E 6 P Q X M P L L M P P Q X X Q P P M L
A sequence of operations on a priority queue that is implemented using unordered array (left), ordered array (middle), and reverse ordered array (right)
What are runtimes of inserting and finding and deleting max?
pq: implementations cost summary
Order of growth of running time for priority queue with \(N\) items
implementation
insert
delMax
max
unordered array
\(1\)
\(N\)
\(N\)
ordered array
\(N\)
\(1\)
\(1\)
goal for today
\(\log N\)
\(\log N\)
\(\log N\)
Priority Queues
Binary Heaps
complete binary tree
Binary tree
Empty or node with links to left and right binary trees
Complete tree
Perfectly balanced tree, except for bottom level
Property: Height of complete binary tree with \(N\) nodes is \(\lfloor \lg N \rfloor\).
Pf: Height increases only when \(N\) is a power of \(2\).
complete binary tree in nature
God loves binary trees
complete binary tree: array representation
Array representation
Indices start at 1
Take nodes in level order
Children of node \(k\) at locations \(2k\) and \(2k+1\)
No explicit links needed!
complete binary tree: array representation
0 1 2 3 4 5 6 7 8 9 10 11
a[i] = [ . T S R P N O A E I H G]
T
S R
P N O A
E I H G
\(^*\) amortized
sweet spot for \(d\) is \(d=4\)
why is last line impossible?
binary heap: considerations
Underflow and overflow
Underflow: throw exception if deleting from empty PQ
Overflow: add no-arg constructor and use resizing array (leads to \(\lg N\) amortized time per op; how to make worst case?)
Minimum-oriented priority queue
Replace less() with greater()
Implement greater()
binary heap: considerations
Binary heap is not cache friendly (ex: page size = 8 nodes)
Cache-aligned \(d\)-heap
Funnel heap
B-heap
...
binary heap: considerations
Other operations
Remove an arbitrary item
Change the priority of an item
Can implement both efficiently with sink() and swim()
Immutability of keys
Assumption: client does not change keys while they're on the PQ
Best practice: use immutable keys
immutability: implementing in Java
Data type: set of values and operations on those values
Immutable data type: cannot change the value once created
public final class Vector { // final => can't override instance methods
private final int N; // instance variables private and final
private final double[] data; // ...
public Vector(double[] data) {
this.N = data.length;
this.data = new double[N];
for(int i = 0; i < N; i++)
this.data[i] = data[i]; // defensive copy of mutable variables
}
/* ... */ // instance methods don't change instance vars
}
Safe to use as key in priority queue or symbol table
Disadvantage: Must create new object for each data type value
Warning
Do NOT use immutable data types on variables that change often.
ex: concatenating a bunch of Strings (use StringBuilder); using Integer for for-loop (use int).
immutability: implementing in Java
“
Classes should be immutable unless there's a very good reason to make them mutable. [...] If a class cannot be made immutable, you should still limit its mutability as much as possible.
—Joshua Bloch (Java architect)
What are the properties of this sorting algorithm?
public void sort(String[] a) {
int N = a.length;
MaxPQ<String> pq = new MaxPQ<String>();
for(int i = 0; i < N; i++) pq.insert(a[i]);
for(int i = N-1; i >= 0; i--) a[i] = pq.delMax();
}
\(N \lg N\) compares in the worst case
in-place sorting
stable sorting
All of the above
heapsort
Basic plan for in-place sort
View input array as a complete binary tree
Heap construction: build a max-heap with all \(N\) keys
Sortdown: repeatedly remove the maximum key
heapsort demo
Heap construction: build max heap using bottom-up method (we assume array entries are indexed 1 to N)
Sortdown: Repeatedly delete the largest remaining item
sink(5)
sink(5)
sink(4)
sink(4)
sink(3)
sink(3)
sink(2)
sink(2)
sink(1)
sink(1)
max-heap!
exch(1, 11), then sink(1)
exch(1, 10), then sink(1)
exch(1, 10), then sink(1)
exch(1, 9), then sink(1)
exch(1, 9), then sink(1)
exch(1, 8), then sink(1)
exch(1, 8), then sink(1)
exch(1, 7), then sink(1)
exch(1, 7), then sink(1)
exch(1, 6), then sink(1)
exch(1, 6), then sink(1)
exch(1, 5), then sink(1)
exch(1, 5), then sink(1)
exch(1, 4), then sink(1)
exch(1, 4), then sink(1)
exch(1, 3), then sink(1)
exch(1, 3), then sink(1)
exch(1, 2), then sink(1)
exch(1, 2), then sink(1)
exch(1, 1), then sink(1)
done sorting!
heapsort: heap construction
Heap construction (first pass):
Build heap using bottom-up method
for(int k = N/2; k >= 1; k--)
sink(a, k, N);
Sortdown (second pass):
Remove the maximum, one at a time
Leave in array, instead of nulling out
while(N > 1) {
exch(a, 1, N--);
sink(a, 1, N);
}
heapsort: java implementation
public class Heap {
public static void sort(Comparable[] a) {
int N = a.length;
for(int k = N/2; k >= 1; k--) sink(a, k, N);
while(N > 1) {
exch(a, 1, N);
sink(a, 1, --N);
}
}
private static void sink(Comparable[] a, int k, int N) {
/* as before, but make static and pass arguments */
}
private static boolean less(Comparable[] a, int i, int j) {
/* as before, but convert from 1-based indexing to 0-base */
}
private static void exch(Object[] a, int i, int j) {
/* as before, but convert from 1-based indexing to 0-base */
}
}
heapsort: trace
N k 0 1 2 3 4 5 6 7 8 9 10 11
S O R T E X A M P L E initial values
11 5 . . . . L . . . . E E
11 4 . . . T . . . M P . .
11 3 . . X . . R A . . . .
11 2 . T . P L . . M O . .
11 1 X T S . . R A . . . .
X T S P L R A M O E E heap-ordered
10 1 T P S O L . . M E . X
9 1 S P R . . E A . . T .
8 1 R P E . . E A . S . .
7 1 P O E M L . . R . . .
6 1 O M E A L . P . . . .
5 1 M L . A . O . . . . .
4 1 L E E A M . . . . . .
3 1 E A E L . . . . . . .
2 1 E A E . . . . . . . .
1 1 A E . . . . . . . . .
A E E L M O P R S T X sorted result
Left side of \(\leq\) is a tricky sum (see Discrete Math)
heapsort: mathematical analysis
Proposition: Heapsort uses \(\leq 2N \lg N\) compares and exchanges, though algorithm can be improved to \(\sim 1 N \lg N\) (but no such variant is known to be practical, see Adaptive heap sort)
Significance: In-place sorting algorithm with \(N \log N\) worst-case
Mergesort: no, linear extra space (in-place merge possible, not practical)
Quicksort: no, quadratic time in worst case (\(N \log N\) worst-case quicksort possible, but not practical)
Heapsort: yes!
heapsort: mathematical analysis
Proposition: Heapsort uses \(\leq 2N \lg N\) compares and exchanges, though algorithm can be improved to \(\sim 1 N \lg N\) (but no such variant is known to be practical, see Adaptive heap sort)
Bottom line: Heapsort is optimal for both time and space, but...
Inner loop longer than quicksort's
Makes poor use of cache (can be improved using advanced caching tricks)
Not stable
Introsort
Goal: as fast as quicksort in practice; \(N \log N\) worst case; in place
Introsort
Run quicksort
Cutoff to heapsort if stack depth exceeds \(2 \lg N\)
Cutoff to insertion sort for \(N = 16\)
In the wild: C++ STL, Microsoft .NET Framework, Go
sorting algorithms: summary
sort
inplace?
stable?
best
avg
worst
remarks
selection
✔
\(\onehalf N^2\)
\(\onehalf N^2\)
\(\onehalf N^2\)
\(N\) exchanges
insertion
✔
✔
\(N\)
\(\onequarter N^2\)
\(\onehalf N^2\)
use for small \(N\) or partially ordered
shell
✔
\(N \log_3 N\)
?
\(c N^a\)
tight code; subquadratic
merge
✔
\(\onehalf N \lg N\)
\(N \lg N\)
\(N \lg N\)
\(N \log N\) guarantee; stable
timsort
✔
\(N\)
\(N \lg N\)
\(N \lg N\)
improves mergesort when preexisting order
quick
✔
\(N \lg N\)
\(2 N \ln N\)
\(\onehalf N^2\)
\(N \log N\) probabilistic guarantee; fastest in practice