Algorithms and Data Structures
Instructors:
Bob Sedgewick Kevin Wayne
Computer Science 226
Fall 2007
outline
why study algorithms?
usual suspects
coursework
resources (web)
resources (books)
Course Overview
COS 226 course overview
What is COS 226?
• Intermediate-level survey course.
• Programming and problem solving with applications.
• Algorithm: method for solving a problem.
• Data structure: method to store information.
Topic Data Structures and Algorithms
data types stack, queue, list, union-find, priority queue sorting quicksort, mergesort, heapsort, radix sorts searching hash table, BST, red-black tree, B-tree
graphs BFS, DFS, Prim, Kruskal, Dijkstra
strings KMP, Rabin-Karp, TST, Huffman, LZW
geometry Graham scan, k-d tree, Voronoi diagram
Why study algorithms?
Their impact is broad and far-reaching
Internet. Web search, packet routing, distributed file sharing.
Biology. Human genome project, protein folding.
Computers. Circuit layout, file system, compilers.
Computer graphics. Movies, video games, virtual reality.
Security. Cell phones, e-commerce, voting machines.
Multimedia. CD player, DVD, MP3, JPG, DivX, HDTV.
Transportation. Airline crew scheduling, map routing.
Physics. N-body simulation, particle collision simulation.
…
Old roots, new opportunities
Study of algorithms dates at least to Euclid
Some important algorithms were discovered by undergraduates!
Why study algorithms?
300 BC
1920s 1940s 1950s 1960s 1970s 1980s 1990s
Why study algorithms?
To be able solve problems that could not otherwise be addressed Example: Network connectivity
[stay tuned]
Why study algorithms?
For intellectual stimulation
They may unlock the secrets of life and of the universe.
Computational models are replacing mathematical models in scientific enquiry
Why study algorithms?
20th century science
(formula based) 21st century science
(algorithm based)
for (double t = 0.0; true; t = t + dt) for (int i = 0; i < N; i++)
{
bodies[i].resetForce();
for (int j = 0; j < N; j++) if (i != j)
bodies[i].addForce(bodies[j]);
}
E = mc2
F = ma F =
Gm1m2 r2 h2
2m2+V(r) (r) = E (r)
For fun and profit
Why study algorithms?
• Their impact is broad and far-reaching
• Old roots, new opportunities
• To be able to solve problems that could not otherwise be addressed
• For intellectual stimulation
• They may unlock the secrets of life and of the universe
• For fun and profit
Why study algorithms?
The Usual Suspects
Lectures: Bob Sedgewick
• TTh 11-12:20, Bowen 222
• Office hours T 3-5 at Cafe Viv in Frist
Course management (everything else): Kevin Wayne Precepts: Kevin Wayne
• Thursdays.
1: 12:30 Friend 110 2: 3:30 Friend 109
• Discuss programming assignments, exercises, lecture material.
• First precept meets Thursday 9/20
• Kevin’s office hours TBA
Need a precept time? Need to change precepts?
• email Donna O’Leary (CS ugrad coordinator)
[email protected]
Coursework and Grading
7-8 programming assignments. 45%
• Due 11:55pm, starting Monday 9/24.
• Available via course website.
Weekly written exercises. 15%
• Due at beginning of Wednesday lecture, starting 9/24.
• Available via course website.
Exams.
• Closed-book with cheatsheet.
• Midterm. 15%
• Final. 25%
Staff discretion. Adjust borderline cases.
• Participation in lecture and precepts
• Everyone needs to meet us both at office hours!
Challenge for the bored. Determine importance of 45-15-15-25 weights
Final
Midterm Programs
HW
Course content.
http://www.princeton.edu/~cos226
• syllabus
• exercises
• lecture slides
• programming assignments (description, code, test data, checklists) Course administration.
https://moodle.cs.princeton.edu/course/view.php?id=24
• programming assignment submission.
• grades.
Booksites.
http://www.cs.princeton.edu/IntroCS http://www.cs.princeton.edu/IntroAlgsDS
• brief summary of content.
• code.
•
Resources (web)
Algorithms in Java, 3
rdedition
• Parts 1-4. [sorting, searching]
• Part 5. [graph algorithms]
Introduction to Programming in Java
• basic programming model
• elementary AofA and data structures Algorithms in Pascal(!)/C/C++, 2
ndedition
• strings
• elementary geometric algorithms
Algorithms, 4
thedition (in preparation)
Resources (books)
Union-Find
network connectivity
quick find quick union
improvements
applications
Union-Find Algorithms
Subtext of today’s lecture (and this course)
Steps to developing a usable algorithm.
• Define the problem.
• Find an algorithm to solve it.
• Fast enough?
• If not, figure out why.
• Find a way to address the problem.
• Iterate until satisfied.
The scientific method
Mathematical models and computational complexity
READ Chapter One of Algs in Java
network connectivity
quick find quick union
improvements
applications
Network connectivity
Basic abstractions
• set of objects
• union command: connect two objects
• find query: is there a path connecting one object to another?
Union-find applications involve manipulating objects of all types.
• Computers in a network.
• Web pages on the Internet.
• Transistors in a computer chip.
• Variable name aliases.
• Pixels in a digital photo.
• Metallic sites in a composite system.
When programming, convenient to name them 0 to N-1.
• Hide details not relevant to union-find.
• Integers allow quick access to object-related info.
• Could use symbol table to translate from object names
Objects
use as array index
2 3 4
6 5 9
1
stay tuned
Union-find abstractions
Simple model captures the essential nature of connectivity.
• Objects.
• Disjoint sets of objects.
• Find query: are objects
2and
9in the same set?
• Union command: merge sets containing
3and
8.
0 1 { 2 3 9 } { 5 6 } 7 { 4 8 }
0 1 { 2 3 4 8 9 } { 5-6 } 7
0 1 { 2 3 9 } { 5-6 } 7 { 4-8 }
add a connection between two grid points
subsets of connected grid points
are two grid points connected?
0 1 2 3 4 5 6 7 8 9 grid points
Connected components
Connected component: set of mutually connected vertices Each union command reduces by 1 the number of components
in out 3 4 3 4 4 9 4 9 8 0 8 0 2 3 2 3 5 6 5 6 2 9 5 9 5 9 7 3 7 3
0
2 3
8 4
6 5 9
1
7 union commands 3 = 10-7 components
7
Network connectivity: larger example
find(u, v) ?
u
v
Network connectivity: larger example
63 components
find(u, v) ? true
Union-find abstractions
• Objects.
• Disjoint sets of objects.
• Find queries: are two objects in the same set?
• Union commands: replace sets containing two items by their union Goal. Design efficient data structure for union-find.
• Find queries and union commands may be intermixed.
• Number of operations M can be huge.
• Number of objects N can be huge.
network connectivity
quick find quick union
improvements
applications
Quick-find [eager approach]
Data structure.
• Integer array
id[]of size
N.
• Interpretation:
pand
qare connected if they have the same id.
i 0 1 2 3 4 5 6 7 8 9 id[i] 0 1 9 9 9 6 6 7 8 9
5 and 6 are connected 2, 3, 4, and 9 are connected
Quick-find [eager approach]
Data structure.
• Integer array
id[]of size
N.
• Interpretation:
pand
qare connected if they have the same id.
Find. Check if
pand
qhave the same id.
Union. To merge components containing
pand
q, change all entries with id[p] to
id[q].
i 0 1 2 3 4 5 6 7 8 9 id[i] 0 1 9 9 9 6 6 7 8 9
5 and 6 are connected 2, 3, 4, and 9 are connected
union of 3 and 6
2, 3, 4, 5, 6, and 9 are connected
i 0 1 2 3 4 5 6 7 8 9 id[i] 0 1 6 6 6 6 6 7 8 6
id[3] = 9; id[6] = 6 3 and 6 not connected
problem: many values can change
Quick-find example
3-4 0 1 2 4 4 5 6 7 8 9
4-9 0 1 2 9 9 5 6 7 8 9
8-0 0 1 2 9 9 5 6 7 0 9
2-3 0 1 9 9 9 5 6 7 0 9
5-6 0 1 9 9 9 6 6 7 0 9
5-9 0 1 9 9 9 9 9 7 0 9
7-3 0 1 9 9 9 9 9 9 0 9
4-8 0 1 0 0 0 0 0 0 0 0
6-1 1 1 1 1 1 1 1 1 1 1
problem: many values can change
public class QuickFind {
private int[] id;
public QuickFind(int N) {
id = new int[N];
for (int i = 0; i < N; i++) id[i] = i;
}
public boolean find(int p, int q) {
return id[p] == id[q];
}
public void unite(int p, int q) {
int pid = id[p];
for (int i = 0; i < id.length; i++) if (id[i] == pid) id[i] = id[q];
} }
Quick-find: Java implementation
1 operation
N operations set id of each object to itself
Quick-find is too slow
Quick-find algorithm may take ~MN steps to process M union commands on N objects Rough standard (for now).
• 10
9operations per second.
• 10
9words of main memory.
• Touch all words in approximately 1 second.
Ex. Huge problem for quick-find.
• 10
10edges connecting 10
9nodes.
• Quick-find takes more than 10
19operations.
• 300+ years of computer time!
Paradoxically, quadratic algorithms get worse with newer equipment.
• New computer may be 10x as fast.
• But, has 10x as much memory so problem may be 10x bigger.
• With quadratic algorithm, takes 10x as long!
a truism (roughly) since 1950 !
network connectivity
quick find quick union
improvements
applications
Quick-union [lazy approach]
Data structure.
• Integer array
id[]of size
N.
• Interpretation:
id[i]is parent of
i.
• Root of
iis
id[id[id[...id[i]...]]].
i 0 1 2 3 4 5 6 7 8 9 id[i] 0 1 9 4 9 6 6 7 8 9
4
7
3 5
0 1 9 6 8
2
3's root is 9; 5's root is 6
keep going until it doesn’t change
Quick-union [lazy approach]
Data structure.
• Integer array
id[]of size
N.
• Interpretation:
id[i]is parent of
i.
• Root of
iis
id[id[id[...id[i]...]]].
Find. Check if p and q have the same root.
Union. Set the id of q's root to the id of p's root.
i 0 1 2 3 4 5 6 7 8 9 id[i] 0 1 9 4 9 6 6 7 8 9
4
7
3 5
0 1 9 6 8
2
3's root is 9; 5's root is 6 3 and 5 are not connected
i 0 1 2 3 4 5 6 7 8 9 id[i] 0 1 9 4 9 6 9 7 8 9
4
7
3 5
0 1 9
6
8
2
only one value changes
p q
keep going until it doesn’t change
Quick-union example
3-4 0 1 2 4 4 5 6 7 8 9
4-9 0 1 2 4 9 5 6 7 8 9
8-0 0 1 2 4 9 5 6 7 0 9
2-3 0 1 9 4 9 5 6 7 0 9
5-6 0 1 9 4 9 6 6 7 0 9
5-9 0 1 9 4 9 6 9 7 0 9
7-3 0 1 9 4 9 6 9 9 0 9
4-8 0 1 9 4 9 6 9 9 0 0
6-1 1 1 9 4 9 6 9 9 0 0
problem: trees can get tall
Quick-union: Java implementation
time proportional to depth of p and q
time proportional to depth of p and q time proportional to depth of i
public class QuickUnion {
private int[] id;
public QuickUnion(int N) {
id = new int[N];
for (int i = 0; i < N; i++) id[i] = i;
}
private int root(int i) {
while (i != id[i]) i = id[i];
return i;
}
public boolean find(int p, int q) {
return root(p) == root(q);
}
public void unite(int p, int q) {
int i = root(p);
int j = root(q);
id[i] = j;
} }
Quick-union is also too slow
Quick-find defect.
• Union too expensive (N steps).
• Trees are flat, but too expensive to keep them flat.
Quick-union defect.
• Trees can get tall.
• Find too expensive (could be N steps)
• Need to do find to do union
algorithm union find
Quick-find N 1
Quick-union N* N worst case
* includes cost of find
network connectivity
quick find quick union
improvements
applications
Improvement 1: Weighting
Weighted quick-union.
• Modify quick-union to avoid tall trees.
• Keep track of size of each component.
• Balance by linking small tree below large one.
Ex. Union of
5and
3.
• Quick union: link
9to
6.
• Weighted quick union: link
6to
9.
4
7
3
5
0 1 9 6 8
2
p q
4 2
1
1 1 1
size
Weighted quick-union example
3-4 0 1 2 3 3 5 6 7 8 9
4-9 0 1 2 3 3 5 6 7 8 3
8-0 8 1 2 3 3 5 6 7 8 3
2-3 8 1 3 3 3 5 6 7 8 3
5-6 8 1 3 3 3 5 5 7 8 3
5-9 8 1 3 3 3 3 5 7 8 3
7-3 8 1 3 3 3 3 5 3 8 3
4-8 8 1 3 3 3 3 5 3 3 3
6-1 8 3 3 3 3 3 5 3 3 3
no problem: trees stay flat
Weighted quick-union: Java implementation
Java implementation.
• Almost identical to quick-union.
• Maintain extra array
sz[]to count number of elements in the tree rooted at i.
Find. Identical to quick-union.
Union. Modify quick-union to
• merge smaller tree into larger tree
• update the
sz[]array.
Weighted quick-union analysis
Analysis.
• Find: takes time proportional to depth of
pand
q.
• Union: takes constant time, given roots.
• Fact: depth is at most lg N. [needs proof]
Stop at guaranteed acceptable performance? No, easy to improve further.
Data Structure Union Find
Quick-find N 1
Quick-union N * N
Weighted QU lg N * lg N
* includes cost of find
Path compression. Just after computing the root of
i, set the
idof each examined node to
root(i).
Improvement 2: Path compression
2
4 11
10 2
5 4
7
8
11 10
root(9)
0
1
0
3
6
9
9
7 8
3 1 6
5
Path compression.
• Standard implementation: add second loop to
root()to set the id of each examined node to the root.
• Simpler one-pass variant: make every other node in path point to its grandparent.
In practice. No reason not to! Keeps tree almost completely flat.
Weighted quick-union with path compression
only one extra line of code !
public int root(int i) {
while (i != id[i]) {
id[i] = id[id[i]];
i = id[i];
}
return i;
}
Weighted quick-union with path compression
3-4 0 1 2 3 3 5 6 7 8 9 4-9 0 1 2 3 3 5 6 7 8 3 8-0 8 1 2 3 3 5 6 7 8 3 2-3 8 1 3 3 3 5 6 7 8 3 5-6 8 1 3 3 3 5 5 7 8 3 5-9 8 1 3 3 3 3 5 7 8 3 7-3 8 1 3 3 3 3 5 3 8 3 4-8 8 1 3 3 3 3 5 3 3 3 6-1 8 3 3 3 3 3 3 3 3 3
no problem: trees stay VERY flat
WQUPC performance
Theorem. Starting from an empty data structure, any sequence
of M union and find operations on N objects takes O(N + M lg* N) time.
• Proof is very difficult.
• But the algorithm is still simple!
Linear algorithm?
• Cost within constant factor of reading in the data.
• In theory, WQUPC is not quite linear.
• In practice, WQUPC is linear.
Amazing fact:
• In theory, no linear linking strategy exists
because lg* N is a constant in this universe
number of times needed to take the lg of a number until reaching 1
N lg* N
1 0
2 1
4 2
16 3
65536 4 265536 5
Summary
Ex. Huge practical problem.
• 10
10edges connecting 10
9nodes.
• WQUPC reduces time from 3,000 years to 1 minute.
• Supercomputer won't help much.
• Good algorithm makes solution possible.
Bottom line.
WQUPC makes it possible to solve problems
M union-find ops on a set of N objects Algorithm Worst-case time
Quick-find M N
Quick-union M N
Weighted QU N + M log N Path compression N + M log N Weighted + path (M + N) lg* N
WQUPC on Java cell phone beats QF on supercomputer!
network connectivity
quick find quick union
improvements
applications
Union-find applications
Network connectivity.
• Percolation.
• Image processing.
• Least common ancestor.
• Equivalence of finite state automata.
• Hinley-Milner polymorphic type inference.
• Kruskal's minimum spanning tree algorithm.
• Games (Go, Hex)
• Compiling equivalence statements in Fortran.
Percolation
A model for many physical systems
• N-by-N grid.
• Each square is vacant or occupied.
• Grid percolates if top and bottom are connected by vacant squares.
percolates does not percolate
model system vacant site occupied site percolates
electricity material conductor insulated conducts
fluid flow material empty blocked porous
social interaction population person empty communicates
Percolation phase transition
Likelihood of percolation depends on site vacancy probability p
Experiments show a threshold p*
• p > p*: almost certainly percolates
• p < p*: almost certainly does not percolate Q. What is the value of p* ?
p high: percolates p low: does not percolate
p*
• Initialize whole grid to be “not vacant”
• Implement “make site vacant” operation that does union() with adjacent sites
• Make all sites on top and bottom rows vacant
• Make random sites vacant until find(top, bottom)
• Vacancy percentage estimates p*
UF solution to find percolation threshold
0 0 0 0 0 0 0 0
2 3 4 5 6 7 8 9
15 20 21
28 29 30 31 33
39 40 42 43 45
50 52 54 55 56 57
1 1 1 1 1 1 1 1
0 0 0 0
10 11 12 0 22 23 24 0 34 35 36 0
46 1 49
58 1
1 1 1 1 not vacant
vacant top
bottom
14 14 14 14
32
32 1
1 1 1
1 1
16 16 16 16
Q. What is percolation threshold p* ?
A. about 0.592746 for large square lattices.
Q. Why is UF solution better than solution in IntroProgramming 2.4?
Percolation
percolation constant known only via simulation
percolates does not percolate
Hex
Hex.
[Piet Hein 1942, John Nash 1948, Parker Brothers 1962]• Two players alternate in picking a cell in a hex grid.
• Black: make a black path from upper left to lower right.
• White: make a white path from lower left to upper right.
Union-find application. Algorithm to detect when a player has won.
Reference: http://mathworld.wolfram.com/GameofHex.html
Subtext of today’s lecture (and this course)
Steps to developing an usable algorithm.
• Define the problem.
• Find an algorithm to solve it.
• Fast enough?
• If not, figure out why.
• Find a way to address the problem.
• Iterate until satisfied.
The scientific method
Mathematical models and computational complexity
READ Chapter One of Algs in Java
Collaboration policy
Exceptions
• Code from course materials OK [cite source]
• Coding with partner OK after first assignment [stay tuned]
Where to get help
• Email (but no code in email)
• Office hours
• Lab TAs in Friend 008/009
• Bounce ideas (but not code) off classmates
Note: Programming in groups except as above is a serious violation.
• working with classmates is encouraged
• checking solutions is OK
Programs: Do not use someone else’s code unless specifically authorized
Exercises: Write up your own solutions (no copying)
Stacks and Queues
stacks
dynamic resizing
queues generics
applications
Stacks and Queues
Fundamental data types.
• Values: sets of objects
• Operations: insert, remove, test if empty.
• Intent is clear when we insert.
• Which item do we remove?
Stack.
• Remove the item most recently added.
• Analogy: cafeteria trays, Web surfing.
Queue.
• Remove the item least recently added.
• Analogy: Registrar's line.
FIFO = "first in first out"
LIFO = "last in first out"
enqueue dequeue
pop push
Client, Implementation, Interface
Separate interface and implementation so as to:
• Build layers of abstraction.
• Reuse software.
• Ex: stack, queue, symbol table.
Interface: description of data type, basic operations.
Client: program using operations defined in interface.
Implementation: actual code implementing operations.
Client, Implementation, Interface Benefits.
• Client can't know details of implementation
client has many implementation from which to choose.
• Implementation can't know details of client needs many clients can re-use the same implementation.
• Design: creates modular, re-usable libraries.
• Performance: use optimized implementation where it matters.
Interface: description of data type, basic operations.
Client: program using operations defined in interface.
Implementation: actual code implementing operations.
stacks
dynamic resizing
queues generics
applications
Stack operations.
• push() Insert a new item onto stack.
• pop() Remove and return the item most recently added.
• isEmpty() Is the stack empty?
Stacks
pop push
a sample stack client
public static void main(String[] args) {
StackOfStrings stack = new StackOfStrings();
while(!StdIn.isEmpty()) {
String s = StdIn.readString();
stack.push(s);
}
while(!stack.isEmpty()) {
String s = stack.pop();
StdOut.println(s);
}
}
Stack pop: Linked-list implementation
best the was it
best the was it first = first.next;
best the was it return item;
first first first
of item = first.item;
Stack push: Linked-list implementation
best the was it
second
best the was it
best the was it
first of
second = first;
first.item = item;
first.next = second;
best the was it
second
first = new Node();
first second first
first
Stack: Linked-list implementation
"inner class"
public class StackOfStrings {
private Node first = null;
private class Node {
String item;
Node next;
}
public boolean isEmpty() { return first == null; } public void push(String item) {
Node second = first;
first = new Node();
first.item = item;
first.next = second;
}
public String pop() {
String item = first.item;
first = first.next;
return item;
} }
Error conditions?
Example: pop() an empty stack
COS 217: bulletproof the code
COS 226: first find the code we want to use
Stack: Array implementation
Array implementation of a stack.
• Use array s[] to store N items on stack.
• push() add new item at s[N] .
• pop() remove item from s[N-1] .
it was the best
0 1 2 3 4 5 6 7 8 9
s[]
N
Stack: Array implementation
avoid loitering
(garbage collector only reclaims memory if no outstanding references)
public class StackOfStrings {
private String[] s;
private int N = 0;
public StringStack(int capacity) { s = new String[capacity]; } public boolean isEmpty()
{ return N == 0; }
public void push(String item) { s[N++] = item; }
public String pop() {
String item = s[N-1];
s[N-1] = null;
N--;
return item;
} }
stacks
dynamic resizing
queues generics
applications
Stack array implementation: Dynamic resizing
Q. How to grow array when capacity reached?
Q. How to shrink array (else it stays big even when stack is small)?
First try:
• push(): increase size of s[] by 1
• pop() : decrease size of s[] by 1 Too expensive
• Need to copy all of the elements to a new array.
• Inserting N elements: time proportional to 1 + 2 + … + N N
2/2.
Need to guarantee that array resizing happens infrequently
infeasible for large N
Q. How to grow array?
A. Use repeated doubling:
if array is full, create a new array of twice the size, and copy items
Consequence. Inserting N items takes time proportional to N (not N
2).
public StackOfStrings() { this(8); }
public void push(String item) {
if (N >= s.length) resize();
s[N++] = item;
}
private void resize(int max) {
String[] dup = new String[max];
for (int i = 0; i < N; i++) dup[i] = s[i];
s = dup;
}
Stack array implementation: Dynamic resizing
no-argument constructor
create new array copy items to it
Q. How (and when) to shrink array?
How: create a new array of half the size, and copy items.
When (first try): array is half full?
No, causes thrashing
When (solution): array is 1/4 full (then new array is half full).
Consequences.
• any sequence of N ops takes time proportional to N
public String pop(String item) {
String item = s[--N];
sa[N] = null;
if (N == s.length/4) resize(s.length/2);
return item;
}
Stack array implementation: Dynamic resizing
Not a.length/2 to avoid thrashing
(push-pop-push-pop-... sequence: time proportional to N for each op)
Stack Implementations: Array vs. Linked List
Stack implementation tradeoffs. Can implement with either array or linked list, and client can use interchangeably. Which is better?
Array.
• Most operations take constant time.
• Expensive doubling operation every once in a while.
• Any sequence of N operations (starting from empty stack) takes time proportional to N.
Linked list.
• Grows and shrinks gracefully.
• Every operation takes constant time.
• Every operation uses extra space and time to deal with references.
Bottom line: tossup for stacks
but differences are significant when other operations are added
"amortized" bound
Stack implementations: Array vs. Linked list
Which implementation is more convenient?
array? linked list?
return count of elements in stack remove the kth most recently added
sample a random element
stacks
dynamic resizing
queues generics
applications
Queue operations.
• enqueue() Insert a new item onto queue.
• dequeue() Delete and return the item least recently added.
• isEmpty() Is the queue empty?
Queues
public static void main(String[] args) {
QueueOfStrings q = new QueueOfStrings();
q.enqueue("Vertigo");
q.enqueue("Just Lose It");
q.enqueue("Pieces of Me");
q.enqueue("Pieces of Me");
System.out.println(q.dequeue());
q.enqueue("Drop It Like It's Hot");
while(!q.isEmpty()
System.out.println(q.dequeue());
}
Dequeue: Linked List Implementation
was the best of
was the best of first = first.next;
was the best of return item;
first first first
it item = first.item;
last
last
last
Aside:
dequeue (pronounced “DQ”) means “remove from a queue”
deque (pronounced “deck”) is a data structure (see PA 1)
Enqueue: Linked List Implementation
x = new Node();
x.item = item;
x.next = null;
last = x;
last.next = x;
first
it was the best
x
of last
first
it was the best
last
it was the best of
it was the best of
first last x
first last x
Queue: Linked List Implementation
public class QueueOfStrings {
private Node first;
private Node last;
private class Node
{ String item; Node next; } public boolean isEmpty() { return first == null; }
public void enqueue(String item) {
Node x = new Node();
x.item = item;
x.next = null;
if (isEmpty()) { first = x; last = x; } else { last.next = x; last = x; } }
public String dequeue() {
String item = first.item;
first = first.next;
return item;
}
Queue: Array implementation
Array implementation of a queue.
• Use array q[] to store items on queue.
• enqueue() : add new object at q[tail] .
• dequeue() : remove object from q[head] .
• Update head and tail modulo the capacity .
[details: good exercise or exam question]
the best of times
0 1 2 3 4 5 6 7 8 9
q[]
head tail capacity = 10
stacks
dynamic resizing
queues generics
applications
Generics (parameterized data types)
We implemented: StackOfStrings , QueueOfStrings . We also want: StackOfURLs , QueueOfCustomers , etc?
Attempt 1. Implement a separate stack class for each type.
• Rewriting code is tedious and error-prone.
• Maintaining cut-and-pasted code is tedious and error-prone.
@#$*! most reasonable approach until Java 1.5 [hence, used in AlgsJava]
Stack of Objects
We implemented: StackOfStrings , QueueOfStrings . We also want: StackOfURLs , QueueOfCustomers , etc?
Attempt 2. Implement a stack with items of type Object .
• Casting is required in client.
• Casting is error-prone: run-time error if types mismatch.
Stack s = new Stack();
Apple a = new Apple();
Orange b = new Orange();
s.push(a);
s.push(b);
a = (Apple) (s.pop());
run-time errorGenerics
Generics. Parameterize stack by a single type.
• Avoid casting in both client and implementation.
• Discover type mismatch errors at compile-time instead of run-time.
Guiding principles.
• Welcome compile-time errors
• Avoid run-time errors Why?
Stack<Apple> s = new Stack<Apple>();
Apple a = new Apple();
Orange b = new Orange();
s.push(a);
s.push(b);
a = s.pop();
compile-time error
no cast needed in client
parameter
Generic Stack: Linked List Implementation
public class StackOfStrings {
private Node first = null;
private class Node {
String item;
Node next;
}
public boolean isEmpty() { return first == null; } public void push(String item) {
Node second = first;
first = new Node();
first.item = item;
first.next = second;
}
public String pop() {
String item = first.item;
first = first.next;
return item;
} }
public class Stack<Item>
{
private Node first = null;
private class Node {
Item item;
Node next;
}
public boolean isEmpty() { return first == null; } public void push(Item item) {
Node second = first;
first = new Node();
first.item = item;
first.next = second;
}
public Item pop() {
Item item = first.item;
first = first.next;
return item;
} }
Generic type name
Generic stack: array implementation
public class Stack<Item>
{
private Item[] s;
private int N = 0;
public Stack(int cap) { s = new Item[cap]; } public boolean isEmpty() { return N == 0; }
public void push(Item item) { s[N++] = item; }
public String pop() {
Item item = s[N-1];
s[N-1] = null;
N--;
return item;
} }
The way it should be.
public class StackOfStrings {
private String[] s;
private int N = 0;
public StackOfStrings(int cap) { s = new String[cap]; } public boolean isEmpty() { return N == 0; }
public void push(String item) { s[N++] = item; }
public String pop() {
String item = s[N-1];
s[N-1] = null;
N--;
return item;
} }
Generic stack: array implementation
public class Stack<Item>
{
private Item[] s;
private int N = 0;
public Stack(int cap)
{ s = (Item[]) new Object[cap]; } public boolean isEmpty()
{ return N == 0; }
public void push(Item item) { s[N++] = item; }
public String pop() {
Item item = s[N-1];
s[N-1] = null;
N--;
return item;
} }
The way it is: an ugly cast in the implementation.
the ugly cast
Number of casts in good code: 0
Generic data types: autoboxing
Generic stack implementation is object-based.
What to do about primitive types?
Wrapper type.
• Each primitive type has a wrapper object type.
• Ex: Integer is wrapper type for int .
Autoboxing. Automatic cast between a primitive type and its wrapper.
Syntactic sugar. Behind-the-scenes casting.
Bottom line: Client code can use generic stack for any type of data
Stack<Integer> s = new Stack<Integer>();
s.push(17); // s.push(new Integer(17));
int a = s.pop(); // int a = ((int) s.pop()).intValue();
stacks
dynamic resizing
queues generics
applications
Stack Applications
Real world applications.
• Parsing in a compiler.
• Java virtual machine.
• Undo in a word processor.
• Back button in a Web browser.
• PostScript language for printers.
• Implementing function calls in a compiler.
Function Calls
How a compiler implements functions.
• Function call: push local environment and return address.
• Return: pop return address and local environment.
Recursive function. Function that calls itself.
Note. Can always use an explicit stack to remove recursion.
static int gcd(int p, int q) { if (q == 0) return p;
else return gcd(q, p % q);
}
gcd (216, 192)
static int gcd(int p, int q) { if (q == 0) return p;
else return gcd(q, p % q);
}
gcd (192, 24)
static int gcd(int p, int q) { if (q == 0) return p;
else return gcd(q, p % q);
}
gcd (24, 0)
p = 24, q = 0 p = 192, q = 24
p = 216, q = 192
Arithmetic Expression Evaluation
Goal. Evaluate infix expressions.
Two-stack algorithm. [E. W. Dijkstra]
• Value: push onto the value stack.
• Operator: push onto the operator stack.
• Left parens: ignore.
• Right parens: pop operator and two values;
push the result of applying that operator to those values onto the operand stack.
Context. An interpreter!
operand operator
value stack operator stack
Arithmetic Expression Evaluation
% java Evaluate
( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) ) 101.0
public class Evaluate {
public static void main(String[] args) { Stack<String> ops = new Stack<String>();
Stack<Double> vals = new Stack<Double>();
while (!StdIn.isEmpty()) {
String s = StdIn.readString();
if (s.equals("(")) ; else if (s.equals("+")) ops.push(s);
else if (s.equals("*")) ops.push(s);
else if (s.equals(")")) { String op = ops.pop();
if (op.equals("+")) vals.push(vals.pop() + vals.pop());
else if (op.equals("*")) vals.push(vals.pop() * vals.pop());
}
else vals.push(Double.parseDouble(s));
}
StdOut.println(vals.pop());
} }
Correctness
Why correct?
When algorithm encounters an operator surrounded by two values within parentheses, it leaves the result on the value stack.
as if the original input were:
Repeating the argument:
Extensions. More ops, precedence order, associativity.
1 + (2 - 3 - 4) * 5 * sqrt(6 + 7) ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
( 1 + ( 5 * ( 4 * 5 ) ) )
( 1 + ( 5 * 20 ) ) ( 1 + 100 )
101
Stack-based programming languages Observation 1.
Remarkably, the 2-stack algorithm computes the same value if the operator occurs after the two values.
Observation 2.
All of the parentheses are redundant!
Bottom line. Postfix or "reverse Polish" notation.
Applications. Postscript, Forth, calculators, Java virtual machine, …
( 1 ( ( 2 3 + ) ( 4 5 * ) * ) + )
1 2 3 + 4 5 * * +
Jan Lukasiewicz
Stack-based programming languages: PostScript Page description language
• explicit stack
• full computational model
• graphics engine Basics
• %!: “I am a PostScript program”
• literal: “push me on the stack”
• function calls take args from stack
• turtle graphics built in
%!
72 72 moveto 0 72 rlineto 72 0 rlineto 0 -72 rlineto -72 0 rlineto 2 setlinewidth
a PostScript program
Stack-based programming languages: PostScript Data types
• basic: integer, floating point, boolean, ...
• graphics: font, path, ....
• full set of built-in operators Text and strings
• full font support
• show (display a string, using current font)
• cvs (convert anything to a string)
%!
/Helvetica-Bold findfont 16 scalefont setfont 72 168 moveto
(Square root of 2:) show 72 144 moveto
2 sqrt 10 string cvs show
like System.out.print()
like toString()
Square root of 2:
1.4142
Stack-based programming languages: PostScript Variables (and functions)
• identifiers start with /
• def operator associates id with value
• braces
• args on stack
%!
/box {
/sz exch def 0 sz rlineto sz 0 rlineto 0 sz neg rlineto sz neg 0 rlineto } def
72 144 moveto 72 box
288 288 moveto 144 box
2 setlinewidth
function definition
function calls
Stack-based programming languages: PostScript for loop
• “from, increment, to” on stack
• loop body in braces
• for operator
if-else
• boolean on stack
• alternatives in braces
• if operator
... (hundreds of operators)
1 1 20
{ 19 mul dup 2 add moveto 72 box } for
Stack-based programming languages: PostScript
An application: all figures in Algorithms in Java
%!
72 72 translate /kochR
{
2 copy ge { dup 0 rlineto } {
3 div
2 copy kochR 60 rotate 2 copy kochR -120 rotate 2 copy kochR 60 rotate 2 copy kochR
} ifelse pop pop } def
0 0 moveto 81 243 kochR 0 81 moveto 27 243 kochR 0 162 moveto 9 243 kochR 0 243 moveto 1 243 kochR stroke
See page 218
Queue applications
Familiar applications.
• iTunes playlist.
• Data buffers (iPod, TiVo).
• Asynchronous data transfer (file IO, pipes, sockets).
• Dispensing requests on a shared resource (printer, processor).
Simulations of the real world.
• Traffic analysis.
• Waiting times of customers at call center.
• Determining number of cashiers to have at a supermarket.
M/D/1 queuing model
M/D/1 queue.
• Customers are serviced at fixed rate of μ per minute.
• Customers arrive according to Poisson process at rate of per minute.
Q. What is average wait time W of a customer?
Q. What is average number of customers L in system?
Arrival rate Departure rate μ
Infinite queue Server
Pr[Xx] = 1ex
inter-arrival time has exponential distribution
M/D/1 queuing model: example
M/D/1 queuing model: experiments and analysis Observation.
As service rate μ approaches arrival rate , service goes to h***.
Queueing theory (see ORFE 309). W =
2 μ ( μ ) + 1
μ , L = W
Little’s Law
M/D/1 queuing model: event-based simulation
public class MD1Queue {
public static void main(String[] args) {
double lambda = Double.parseDouble(args[0]); // arrival rate double mu = Double.parseDouble(args[1]); // service rate Histogram hist = new Histogram(60);
Queue<Double> q = new Queue<Double>();
double nextArrival = StdRandom.exp(lambda);
double nextService = 1/mu;
while (true) {
while (nextArrival < nextService) {
q.enqueue(nextArrival);
nextArrival += StdRandom.exp(lambda);
}
double wait = nextService - q.dequeue();
hist.addDataPoint(Math.min(60, (int) (wait)));
if (!q.isEmpty())
nextService = nextArrival + 1/mu;
else
nextService = nextService + 1/mu;
} } }
Analysis of Algorithms
overview
experiments
models
case study hypotheses
Updated from:
Algorithms in Java, Chapter 2
Running time
Charles Babbage (1864)
As soon as an Analytic Engine exists, it will necessarily guide the future course of the science. Whenever any result is sought by its aid, the question will arise - By what course of calculation can these results be arrived at by the machine in the shortest time? - Charles Babbage
Analytic Engine
how many times do you have to turn the crank?