Course navigation & on this pageLecture 07 · CS-702
Stacks, queues & amortized analysis
Study abstract data types, linked structures, resizing arrays, generics and expression evaluation.
Intermediate~65 min guideSource-based learning
What you’ll understand
Distinguish LIFO, FIFO and unordered collection behavior.
Trace linked and array implementations.
Explain geometric resizing and amortized bounds.
Understand generics, iteration and two-stack evaluation.
Before you begin: Java objects and references · Arrays · Summations
Expanded study guide
The interface and its implementation
An abstract data type defines values and permitted operations. A client uses the interface without depending on its representation. A stack removes the most recently added item (LIFO); a queue removes the least recently added item (FIFO). A bag supports collecting and iterating items without a removal order.
Separating interface from implementation lets a linked structure and a resizing array serve the same client while making different time and memory tradeoffs.
A stack built from linked nodes
Keep first pointing to the top node. Push creates a node whose next reference is the old first. Pop saves first.item and advances first to first.next. Each operation changes a fixed number of references, so it takes Θ(1) worst-case time.
Pushing “to”, “be”, “or” produces a top-to-bottom order of “or”, “be”, “to”. A pop returns “or”. An empty stack must be handled before dereferencing first.
An array stack and loitering
Store items in positions 0 through n−1. Push writes at index n then increments n. Pop decrements n and returns the former last item. Fixed capacity imposes an overflow limit.
After a pop, clearing the unused reference allows the removed object to be garbage collected if no other references remain. Retaining an unnecessary reference is called loitering. This is different from the abstract stack becoming empty.
Why geometric resizing works
When full, allocate an array twice the current capacity and copy existing items. Shrink to half capacity when occupancy reaches one quarter. Shrinking at one half can cause an alternating push/pop sequence to repeatedly copy the array.
For a growing sequence, the copy sizes form a geometric series. Their total is O(n), so n pushes take O(n) work and each has O(1) amortized cost. A single push that resizes still takes Θ(n). Amortized analysis does not assume random inputs.
1+2+4+⋯+2k=2k+1−1=O(2k)
Queues: first and last
A linked queue removes from first and inserts after last. When enqueueing into an empty queue, first and last must both refer to the new node. After removing the last item, clear last as well.
A circular array queue uses head and tail indices that wrap modulo capacity. Resizing copies items in logical FIFO order, not necessarily in the current physical order. Linked enqueue/dequeue are Θ(1) worst case; resizing-array operations are Θ(1) amortized.
Generics and autoboxing
Stack<Item> supports different element types without duplicating the implementation. Type checking can reject incompatible values before runtime. Primitive values use wrapper types, such as Integer, when placed in generic collections.
Java does not directly permit new Item[capacity]. The source uses a cast from an Object array and discusses the unchecked warning. Preserve that warning’s context; the internal representation and type discipline explain why the implementation can use it.
Iteration without exposing representation
Iterable supplies an iterator() method. Iterator supplies hasNext() and next(). This supports the enhanced for loop while hiding whether the collection uses nodes or an array.
A linked stack iterator follows nodes from first; an array stack iterator walks backwards from n−1. The slides describe fail-fast detection using a modification counter. Not every supplied implementation implements that extension, so inspect the actual class before relying on it.
Function calls and expression evaluation
A function call can push a frame containing its local environment and return address. Returning pops the frame. This explains why recursion consumes stack memory. Other stack uses in the lecture include undo and parsing.
Dijkstra’s two-stack evaluator keeps operators and values separately. Ignore a left parenthesis; push numbers and operators; on a right parenthesis, pop the operator and its operands and push the computed result. For noncommutative operations, operand order matters.
For ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) ), evaluate 2 + 3 = 5 and 4 * 5 = 20, then 5 * 20 = 100 and 1 + 100 = 101. Each token is processed a constant number of times, giving linear time and at most linear auxiliary space.
Tradeoffs to remember
Linked structures avoid resizing pauses but use references per node. Resizing arrays store items compactly but occasionally copy. Distinguish worst-case per-operation cost from amortized cost across a sequence.
Original implementations
Read the complete original code and its documentation. Core algorithm pages add a walkthrough, complexity discussion and a worked example.
Supplementary practice. Try each question before opening the answer.
Push A, push B, pop, push C, pop. What is returned?
The first pop returns B, and the second returns C. A remains on the stack.
Why shrink at one-quarter occupancy rather than one-half?
It leaves room between growth and shrink thresholds, preventing a short alternating sequence from forcing repeated linear-time copies.
What is the result of ( 8 - ( 6 / 3 ) )?
6. The inner division is 6 / 3 = 2, and the outer subtraction is 8 − 2. Popping operands in the wrong order would change the result.
Original course material
Complete lecture source
Every source page is preserved below. Open a page to read its text and inspect the original diagram, formula or example. Expanded explanations above are supplementary.
Page 01 · ROBERT SEDGEWICK | KEVIN WAYNE AlgorithmsOriginal page 1 · Open the image for full detail.
ROBERT SEDGEWICK | KEVIN WAYNE Algorithms
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queues
AlgorithmsF O U R T H E D I T I O N
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 02 · Stacks and queuesOriginal page 2 · Open the image for full detail.
Stacks and queues
Fundamental data types.
collection of objects.・Value:
insert, remove, iterate, test if empty.・Operations:
is clear when we insert.・Intent
item do we remove?・Which
stack
push
pop
queue
enqueue dequeue
Stack. Examine the item most recently added. LIFO = "last in first out"
Queue. Examine the item least recently added. FIFO = "first in first out"
2
Page 03 · Client, implementation, interfaceOriginal page 3 · Open the image for full detail.
Client, implementation, interface
Separate interface and implementation.
Ex: stack, queue, bag, priority queue, symbol table, union-find, .…
Benefits.
can't know details of implementation ⇒ ・Client
client has many implementation from which to choose.
can't know details of client needs ⇒・Implementation
many clients can re-use the same implementation.
creates modular, reusable libraries.・Design:
use optimized implementation where it matters.・Performance:
Client: program using operations defined in interface.
Implementation: actual code implementing operations.
Interface: description of data type, basic operations.
3
Page 04 · 1.3 BAGS, QUEUES, ANDOriginal page 4 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 05 · Stack APIOriginal page 5 · Open the image for full detail.
Stack API
Warmup API. Stack of strings data type.
push pop
public class StackOfStrings
StackOfStrings() create an empty stack
void push(String item) insert a new string onto stack
remove and return the string
String pop()
most recently added
boolean isEmpty() is the stack empty?
int size() number of strings on the stack
Warmup client. Reverse sequence of strings from standard input.
5
Page 06 · Sample clientOriginal page 6 · Open the image for full detail.
Sample client
Warmup client. Reverse sequence of strings from standard input.
string and push onto stack.・Read
string and print. push pop・Pop
public class ReverseStrings
{
public static void main(String[] args)
{
StackOfStrings stack = new StackOfStrings();
while (!StdIn.isEmpty())
stack.push(StdIn.readString());
while (!stack.isEmpty())
StdOut.println(stack.pop());
% more tinyTale.txt
}
it was the best of times ...
}
% java ReverseStrings < tinyTale.txt
... times of best the was it
[ignoring newlines] 6
Page 07 · Stack test clientOriginal page 7 · Open the image for full detail.
Stack test client
Read strings from standard input.
string equals "-", pop string from stack and print.・If
push string onto stack. push pop・Otherwise,
public static void main(String[] args)
{
StackOfStrings stack = new StackOfStrings();
while (!StdIn.isEmpty())
{
String s = StdIn.readString();
if (s.equals("-")) StdOut.print(stack.pop());
else stack.push(s);
}
}
% more tobe.txt
to be or not to - be - -
% java StackOfStrings < tobe.txt
to be not
7
Page 08 · How to implement a stack with a linked list?Original page 8 · Open the image for full detail.
How to implement a stack with a linked list?
A. Can't be done efficiently with a singly-linked list.
top of stack
B. it was the best of null
top of stack
C. of best the was it null
8
Page 09 · Stack: linked-list implementationOriginal page 9 · Open the image for full detail.
Stack: linked-list implementation
pointer first to first node in a singly-linked list.・Maintain
new item before first.・Push
item from first. ・Pop
top of stack
of best the was it null
first
9
Page 10 · Stack pop: linked-list implementationOriginal page 10 · Open the image for full detail.
Stack pop: linked-list implementation
save item to return
String item = first.item;
delete first node
inner class first = first.next;
private class Node
first or
{ be to
null
String item;
Node next; first
or
} be to
null
return saved item
return item;
10
Page 11 · Stack push: linked-list implementationOriginal page 11 · Open the image for full detail.
Stack push: linked-list implementation
save a link to the list
Node oldfirst = first;
oldfirst
first or
be
to
null
inner class create a new node for the beginning
first = new Node(); private class Node
oldfirst
{
first
String item; or
be
to
Node next; null
}
set the instance variables in the new node
first.item = "not";
first.next = oldfirst;
first not
or
be
to
null
11
Page 12 · Stack: linked-list implementation in JavaOriginal page 12 · Open the image for full detail.
Stack: linked-list implementation in Java
public class LinkedStackOfStrings
{
private Node first = null;
private class Node private inner class
{
(access modifiers for instance
String item;
variables don't matter)
Node next;
}
public boolean isEmpty()
{ return first == null; }
public void push(String item)
{
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
}
public String pop()
{
String item = first.item;
12
Page 13 · Stack: linked-list implementation performanceOriginal page 13 · Open the image for full detail.
Stack: linked-list implementation performance
Proposition. Every operation takes constant time in the worst case.
Proposition. A stack with N items uses ~ 40 N bytes.
inner class object 16 bytes (object overhead)
overhead
private class Node
extra
8 bytes (inner class extra overhead) overhead {
item 8 bytes (reference to String) String item;
references
Node next; next 8 bytes (reference to Node)
}
40 bytes per stack node
Remark. This accounts for the memory for the stack
(but not the memory for strings themselves, which the client owns).
13
Page 14 · How to implement a fixed-capacity stack with an array?Original page 14 · Open the image for full detail.
How to implement a fixed-capacity stack with an array?
A. Can't be done efficiently with an array.
top of stack
it was the best of times null null null nullB.
0 1 2 3 4 5 6 7 8 9
top of stack
times of best the was it null null null nullC.
0 1 2 3 4 5 6 7 8 9
14
Page 15 · Fixed-capacity stack: array implementationOriginal page 15 · Open the image for full detail.
Fixed-capacity stack: array implementation
array s[] to store N items on stack.・Use
・push(): add new item at s[N].
・pop(): remove item from s[N-1].
top of stack
it was the best of times null null null null s[]
0 1 2 3 4 5 6 7 8 9
N capacity = 10
Defect. Stack overflows when N exceeds capacity. [stay tuned]
15
Page 16 · Fixed-capacity stack: array implementationOriginal page 16 · Open the image for full detail.
Fixed-capacity stack: array implementation
public class FixedCapacityStackOfStrings
{ a cheat
private String[] s; (stay tuned)
private int N = 0;
public FixedCapacityStackOfStrings(int capacity)
{ s = new String[capacity]; }
public boolean isEmpty()
{ return N == 0; }
public void push(String item)
use to index into array;
{ s[N++] = item; }
then increment N
public String pop()
{ return s[--N]; } decrement N;
then use to index into array }
16
Page 17 · Stack considerationsOriginal page 17 · Open the image for full detail.
Stack considerations
Overflow and underflow.
throw exception if pop from an empty stack.・Underflow:
use resizing array for array implementation. [stay tuned]・Overflow:
Null items. We allow null items to be inserted.
Loitering. Holding a reference to an object when it is no longer needed.
public String pop() public String pop()
{ return s[--N]; } {
String item = s[--N]; loitering
s[N] = null;
return item;
}
this version avoids "loitering":
garbage collector can reclaim memory for
an object only if no outstanding references
17
Page 18 · 1.3 BAGS, QUEUES, ANDOriginal page 18 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 19 · 1.3 BAGS, QUEUES, ANDOriginal page 19 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 20 · Stack: resizing-array implementationOriginal page 20 · Open the image for full detail.
Stack: resizing-array implementation
Problem. Requiring client to provide capacity does not implement API!
Q. How to grow and shrink array?
First try.
s[] by 1.・push(): increase size of array
s[] by 1.・pop(): decrease size of array
Too expensive. infeasible for large N
to copy all items to a new array, for each operation.・Need
accesses to insert first N items = N + (2 + 4 + … + 2(N – 1)) ~ N 2.・Array
1 array access 2(k–1) array accesses to expand to size k
per push (ignoring cost to create new array)
Challenge. Ensure that array resizing happens infrequently.
20
Page 21 · Stack: resizing-array implementationOriginal page 21 · Open the image for full detail.
Stack: resizing-array implementation
"repeated doubling"
Q. How to grow array?
A. If array is full, create a new array of twice the size, and copy items.
public ResizingArrayStackOfStrings()
{ s = new String[1]; }
public void push(String item)
{
if (N == s.length) resize(2 * s.length);
s[N++] = item;
}
private void resize(int capacity)
{
String[] copy = new String[capacity];
for (int i = 0; i < N; i++)
copy[i] = s[i];
s = copy;
Array accesses to insert first N = 2i items. N + (2 + 4 + 8 + … + N) ~ 3N.
}
1 array access k array accesses to double to size k
per push (ignoring cost to create new array) 21
Page 22 · Stack: amortized cost of adding to a stackOriginal page 22 · Open the image for full detail.
Stack: amortized cost of adding to a stack
Cost of inserting first N items. N + (2 + 4 + 8 + … + N) ~ 3N.
1 array access k array accesses to double to size k
per push (ignoring cost to create new array)
128
one gray dot 128
for each operation accesses)
(array
64 cost red dots give cumulative average 3
0
0 number of push() operations 128
22
Page 23 · Stack: resizing-array implementationOriginal page 23 · Open the image for full detail.
Stack: resizing-array implementation
Q. How to shrink array?
First try.
s[] when array is full.・push(): double size of array
s[] when array is one-half full.・pop(): halve size of array
Too expensive in worst case.
push-pop-push-pop-… sequence when array is full.・Consider
operation takes time proportional to N.・Each
N = 5 to be or not to null null null
N = 4 to be or not
N = 5 to be or not to null null null
N = 4 to be or not
23
Page 24 · Stack: resizing-array implementationOriginal page 24 · Open the image for full detail.
Stack: resizing-array implementation
Q. How to shrink array?
Efficient solution.
s[] when array is full.・push(): double size of array
s[] when array is one-quarter full.・pop(): halve size of array
public String pop()
{
String item = s[--N];
s[N] = null;
if (N > 0 && N == s.length/4) resize(s.length/2);
return item;
}
Invariant. Array is between 25% and 100% full.
24
Page 25 · Stack resizing-array implementation: performanceOriginal page 25 · Open the image for full detail.
Stack resizing-array implementation: performance
Amortized analysis. Starting from an empty data structure, average running time
per operation over a worst-case sequence of operations.
Proposition. Starting from an empty stack, any sequence of M push and pop
operations takes time proportional to M.
best worst amortized
construct 1 1 1
push 1 N 1
pop 1 N 1
doubling and
halving operations size 1 1 1
order of growth of running time
for resizing stack with N items
25
Page 26 · Stack resizing-array implementation: memory usageOriginal page 26 · Open the image for full detail.
Stack resizing-array implementation: memory usage
Q. How much memory does a ResizingArrayStackOfStrings use to store N strings in the
best case? Worst case?
Count only the memory owned by the stack (not the strings themselves).
26
Page 27 · Stack resizing-array implementation: memory usageOriginal page 27 · Open the image for full detail.
Stack resizing-array implementation: memory usage
Proposition. Uses between ~ 8 N and ~ 32 N bytes to represent a stack
with N items.
8 N when full.・~
32 N when one-quarter full.・~
public class ResizingArrayStackOfStrings
{
8 bytes × array size
private String[] s;
private int N = 0;
…
}
Remark. This accounts for the memory for the stack
(but not the memory for strings themselves, which the client owns).
27
Page 28 · Stack implementations: resizing array vs. linked listOriginal page 28 · Open the image for full detail.
Stack implementations: resizing array vs. linked list
Tradeoffs. Can implement a stack with either resizing array or linked list;
client can use interchangeably. Which one is better?
Linked-list implementation.
operation takes constant time in the worst case.・Every
extra time and space to deal with the links.・Uses
Resizing-array implementation.
operation takes constant amortized time.・Every
wasted space.・Less
N = 4 to be or not null null null null
first not
or
be
to
null
28
Page 29 · 1.3 BAGS, QUEUES, ANDOriginal page 29 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 30 · Queue APIOriginal page 30 · Open the image for full detail.
Queue API
enqueue
public class QueueOfStrings
QueueOfStrings() create an empty queue
void enqueue(String item) insert a new string onto queue
remove and return the string
String dequeue()
least recently added
boolean isEmpty() is the queue empty?
int size() number of strings on the queue
dequeue
30
Page 31 · How to implement a queue with a linked list?Original page 31 · Open the image for full detail.
How to implement a queue with a linked list?
A. Can't be done efficiently with a singly-linked list.
back of queue front of queue
B.
times of best the was it null
front of queue back of queue
C. it was the best of times null
31
Page 32 · Queue: linked-list implementationOriginal page 32 · Open the image for full detail.
Queue: linked-list implementation
one pointer first to first node in a singly-linked list.・Maintain
another pointer last to last node.・Maintain
from first.・Dequeue
after last.・Enqueue
front of queue back of queue
it was the best of times null
first last
32
Page 33 · Queue dequeue: linked-list implementationOriginal page 33 · Open the image for full detail.
Queue dequeue: linked-list implementation
save item to return
String item = first.item;
delete first node
first = first.next;
inner class last
first to
be
private class Node or
null
{
last
String item; first
to
be
Node next; or
null
}
return saved item
return item;
Remark. Identical code to linked-list stack pop().
33
Page 34 · Queue enqueue: linked-list implementationOriginal page 34 · Open the image for full detail.
Queue enqueue: linked-list implementation
save a link to the last node
Node oldlast = last;
oldlast
last
first to
be
or
null
create a new node for the endinner class
last = new Node();
private class Node last.item = "not";
{ oldlast
last
first to String item; be
or
Node next; null not
null
}
link the new node to the end of the list
oldlast.next = last;
oldlast
last
first to
be
or
not
null
34
Page 35 · Queue: linked-list implementation in JavaOriginal page 35 · Open the image for full detail.
Queue: linked-list implementation in Java
public class LinkedQueueOfStrings
{
private Node first, last;
private class Node
{ /* same as in LinkedStackOfStrings */ }
public boolean isEmpty()
{ return first == null; }
public void enqueue(String item)
{
Node oldlast = last;
special cases for
last = new Node();
empty queue last.item = item;
last.next = null;
if (isEmpty()) first = last;
else oldlast.next = last;
}
public String dequeue()
{
String item = first.item;
first = first.next;
35
if (isEmpty()) last = null;
Page 36 · How to implement a fixed-capacity queue with an array?Original page 36 · Open the image for full detail.
How to implement a fixed-capacity queue with an array?
A. Can't be done efficiently with an array.
front of queue back of queue
it was the best of times null null null nullB.
0 1 2 3 4 5 6 7 8 9
back of queue front of queue
times of best the was it null null null nullC.
0 1 2 3 4 5 6 7 8 9
36
Page 37 · Queue: resizing-array implementationOriginal page 37 · Open the image for full detail.
Queue: resizing-array implementation
array q[] to store items in queue.・Use
・enqueue(): add new item at q[tail].
・dequeue(): remove item from q[head].
head and tail modulo the capacity.・Update
resizing array.・Add
front of queue back of queue
null null the best of times null null null null q[]
0 1 2 3 4 5 6 7 8 9
head tail capacity = 10
Q. How to resize?
37
Page 38 · 1.3 BAGS, QUEUES, ANDOriginal page 38 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 39 · Parameterized stackOriginal page 39 · Open the image for full detail.
Parameterized stack
We implemented: StackOfStrings.
We also want: StackOfURLs, StackOfInts, StackOfVans, ….
Attempt 1. Implement a separate stack class for each type.
code is tedious and error-prone.・Rewriting
cut-and-pasted code is tedious and error-prone. ・Maintaining
@#$*! most reasonable approach until Java 1.5.
39
Page 40 · Parameterized stackOriginal page 40 · Open the image for full detail.
Parameterized stack
We implemented: StackOfStrings.
We also want: StackOfURLs, StackOfInts, StackOfVans, ….
Attempt 2. Implement a stack with items of type Object.
is required in client.・Casting
is error-prone: run-time error if types mismatch.・Casting
StackOfObjects s = new StackOfObjects();
Apple a = new Apple();
Orange b = new Orange();
s.push(a);
s.push(b);
a = (Apple) (s.pop());
run-time error
40
Page 41 · Parameterized stackOriginal page 41 · Open the image for full detail.
Parameterized stack
We implemented: StackOfStrings.
We also want: StackOfURLs, StackOfInts, StackOfVans, ….
Attempt 3. Java generics.
casting in client.・Avoid
type mismatch errors at compile-time instead of run-time.・Discover
type parameter
Stack<Apple> s = new Stack<Apple>();
Apple a = new Apple();
Orange b = new Orange();
s.push(a); compile-time error
s.push(b);
a = s.pop();
Guiding principles. Welcome compile-time errors; avoid run-time errors.
41
Page 42 · Generic stack: linked-list implementationOriginal page 42 · Open the image for full detail.
Generic stack: linked-list implementation
public class LinkedStackOfStrings public class Stack<Item>
{ {
private Node first = null; private Node first = null;
generic type name
private class Node private class Node
{ {
String item; Item item;
Node next; Node next;
} }
public boolean isEmpty() public boolean isEmpty()
{ return first == null; } { return first == null; }
public void push(String item) public void push(Item item)
{ {
Node oldfirst = first; Node oldfirst = first;
first = new Node(); first = new Node();
first.item = item; first.item = item;
first.next = oldfirst; first.next = oldfirst;
} }
public String pop() public Item pop()
{ {
String item = first.item; Item item = first.item;
first = first.next; first = first.next;
return item; return item;
42
} }
Page 43 · Generic stack: array implementationOriginal page 43 · Open the image for full detail.
Generic stack: array implementation
the way it should be
public class FixedCapacityStackOfStrings public class FixedCapacityStack<Item>
{ {
private String[] s; private Item[] s;
private int N = 0; private int N = 0;
public ..StackOfStrings(int capacity) public FixedCapacityStack(int capacity)
{ s = new String[capacity]; } { s = new Item[capacity]; }
public boolean isEmpty() public boolean isEmpty()
{ return N == 0; } { return N == 0; }
public void push(String item) public void push(Item item)
{ s[N++] = item; } { s[N++] = item; }
public String pop() public Item pop()
{ return s[--N]; } { return s[--N]; }
} }
@#$*! generic array creation not allowed in Java
43
Page 44 · Generic stack: array implementationOriginal page 44 · Open the image for full detail.
Generic stack: array implementation
the way it is
public class FixedCapacityStackOfStrings public class FixedCapacityStack<Item>
{ {
private String[] s; private Item[] s;
private int N = 0; private int N = 0;
public ..StackOfStrings(int capacity) public FixedCapacityStack(int capacity)
{ s = new String[capacity]; } { s = (Item[]) new Object[capacity]; }
public boolean isEmpty() public boolean isEmpty()
{ return N == 0; } { return N == 0; }
public void push(String item) public void push(Item item)
{ s[N++] = item; } { s[N++] = item; }
public String pop() public Item pop()
{ return s[--N]; } { return s[--N]; }
} }
the ugly cast
44
Page 45 · Unchecked castOriginal page 45 · Open the image for full detail.
Unchecked cast
% javac FixedCapacityStack.java
Note: FixedCapacityStack.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
% javac -Xlint:unchecked FixedCapacityStack.java
FixedCapacityStack.java:26: warning: [unchecked] unchecked cast
found : java.lang.Object[]
required: Item[]
a = (Item[]) new Object[capacity];
^
1 warning
Q. Why does Java make me cast (or use reflection)?
Short answer. Backward compatibility.
Long answer. Need to learn about type erasure and covariant arrays.
45
Page 46 · Generic data types: autoboxingOriginal page 46 · Open the image for full detail.
Generic data types: autoboxing
Q. What to do about primitive types?
Wrapper type.
primitive type has a wrapper object type.・Each
Integer is wrapper type for int. ・Ex:
Autoboxing. Automatic cast between a primitive type and its wrapper.
Stack<Integer> s = new Stack<Integer>();
s.push(17); // s.push(Integer.valueOf(17));
int a = s.pop(); // int a = s.pop().intValue();
Bottom line. Client code can use generic stack for any type of data.
46
Page 47 · 1.3 BAGS, QUEUES, ANDOriginal page 47 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 48 · IterationOriginal page 48 · Open the image for full detail.
Iteration
Design challenge. Support iteration over stack items by client,
without revealing the internal representation of the stack.
i N
s[] it was the best of times null null null null
0 1 2 3 4 5 6 7 8 9
first current
times of best the was it null
Java solution. Make stack implement the java.lang.Iterable interface.
48
Page 49 · IteratorsOriginal page 49 · Open the image for full detail.
Iterators
java.lang.Iterable interface
Q. What is an Iterable ?
public interface Iterable<Item>
A. Has a method that returns an Iterator.
{
Iterator<Item> iterator();
}
Q. What is an Iterator ?
java.util.Iterator interface
A. Has methods hasNext() and next().
public interface Iterator<Item>
{
boolean hasNext();
Q. Why make data structures Iterable ? Item next(); optional; use
at your own risk
A. Java supports elegant client code. void remove();
}
“foreach” statement (shorthand) equivalent code (longhand)
for (String s : stack) Iterator<String> i = stack.iterator();
StdOut.println(s); while (i.hasNext())
{
String s = i.next();
StdOut.println(s);
} 49
Page 50 · Stack iterator: linked-list implementationOriginal page 50 · Open the image for full detail.
Stack iterator: linked-list implementation
import java.util.Iterator;
public class Stack<Item> implements Iterable<Item>
{
...
public Iterator<Item> iterator() { return new ListIterator(); }
private class ListIterator implements Iterator<Item>
{
private Node current = first;
public boolean hasNext() { return current != null; } throw UnsupportedOperationException
public void remove() { /* not supported */ } throw NoSuchElementException
public Item next() if no more items in iteration
{
Item item = current.item;
current = current.next;
return item;
} first current
}
}
times of best the was it null
50
Page 51 · Stack iterator: array implementationOriginal page 51 · Open the image for full detail.
Stack iterator: array implementation
import java.util.Iterator;
public class Stack<Item> implements Iterable<Item>
{
…
public Iterator<Item> iterator()
{ return new ReverseArrayIterator(); }
private class ReverseArrayIterator implements Iterator<Item>
{
private int i = N;
public boolean hasNext() { return i > 0; }
public void remove() { /* not supported */ }
public Item next() { return s[--i]; }
}
i N
}
s[] it was the best of times null null null null
0 1 2 3 4 5 6 7 8 9
51
Page 52 · Iteration: concurrent modificationOriginal page 52 · Open the image for full detail.
Iteration: concurrent modification
Q. What if client modifies the data structure while iterating?
A. A fail-fast iterator throws a java.util.ConcurrentModificationException.
concurrent modification
for (String s : stack)
stack.push(s);
Q. How to detect?
A.
total number of push() and pop() operations in Stack.・Count
counts in *Iterator subclass upon creation.・Save
when calling next() and hasNext(), the current counts do not equal the saved・If,
counts, throw exception.
52
Page 53 · 1.3 BAGS, QUEUES, ANDOriginal page 53 · Open the image for full detail.
1.3 BAGS, QUEUES, AND
‣ stacks
‣ resizing arrays
‣ queuesAlgorithms
‣ generics
‣ iterators
ROBERT SEDGEWICK | KEVIN WAYNE
http://algs4.cs.princeton.edu ‣ applications
Page 54 · Java collections libraryOriginal page 54 · Open the image for full detail.
Java collections library
List interface. java.util.List is API for an sequence of items.
public interface List<Item> implements Iterable<Item>
List() create an empty list
boolean isEmpty() is the list empty?
int size() number of items
void add(Item item) append item to the end
Item get(int index) return item at given index
Item remove(int index) return and delete item at given index
boolean contains(Item item) does the list contain the given item?
Iterator<Item> iterator() iterator over all items in the list
...
Implementations. java.util.ArrayList uses resizing array;
java.util.LinkedList uses linked list. caveat: only some
operations are efficient 54
Page 55 · Java collections libraryOriginal page 55 · Open the image for full detail.
Java collections library
java.util.Stack.
push(), pop(), and iteration.・Supports
java.util.Vector, which implements java.util.List interface from previous・Extends
slide, including get() and remove().
and poorly-designed API (why?)・Bloated
Java 1.3 bug report (June 27, 2001)
The iterator method on java.util.Stack iterates through a Stack from the bottom up. One would
think that it should iterate as if it were popping off the top of the Stack.
status (closed, will not fix)
It was an incorrect design decision to have Stack extend Vector ("is-a" rather than "has-a"). We
sympathize with the submitter but cannot fix this because of compatibility.
55
Page 56 · Java collections libraryOriginal page 56 · Open the image for full detail.
Java collections library
java.util.Stack.
push(), pop(), and iteration.・Supports
java.util.Vector, which implements java.util.List interface from previous・Extends
slide, including get() and remove().
and poorly-designed API (why?)・Bloated
java.util.Queue. An interface, not an implementation of a queue.
Best practices. Use our implementations of Stack, Queue, and Bag.
56
Page 57 · War story (from Assignment 1)Original page 57 · Open the image for full detail.
War story (from Assignment 1)
Generate random open sites in an N-by-N percolation system.
pick (i, j) at random; if already open, repeat. ・Jenny:
Takes ~ c1 N 2 seconds.
create a java.util.ArrayList of N 2 closed sites. ・Kenny:
Pick an index at random and delete.
Takes ~ c2 N 4 seconds.
Why is my program so slow?
Kenny
Lesson. Don't use a library until you understand its API!
This course. Can't use a library until we've implemented it in class.
57
Page 58 · Stack applicationsOriginal page 58 · Open the image for full detail.
Stack applications
in a compiler.・Parsing
virtual machine.・Java
in a word processor.・Undo
button in a Web browser.・Back
language for printers.・PostScript
function calls in a compiler.・Implementing
・...
58
Page 59 · Function callsOriginal page 59 · Open the image for full detail.
Function calls
How a compiler implements a function.
call: push local environment and return address.・Function
pop return address and local environment.・Return:
Recursive function. Function that calls itself.
Note. Can always use an explicit stack to remove recursion.
gcd (216, 192)
static int gcd(int p, int q) { p = 216, q = 192
if (q == 0) return p; gcd (192, 24)
else return gcd(q, p % q);
} static int gcd(int p, int q) {
p = 192, q = 24
if (q == 0) return p; gcd (24, 0)
else return gcd(q, p % q);
} static int gcd(int p, int q) {
p = 24, q = 0 if (q == 0) return p;
else return gcd(q, p % q);
}
59
Page 60 · Arithmetic expression evaluationOriginal page 60 · Open the image for full detail.
Page 62 · Arithmetic expression evaluationOriginal page 62 · Open the image for full detail.
Arithmetic expression evaluation
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());
}
% java Evaluate else vals.push(Double.parseDouble(s));
( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
} 101.0
StdOut.println(vals.pop());
62
}
Page 63 · CorrectnessOriginal page 63 · Open the image for full detail.
Correctness
Q. Why correct?
A. When algorithm encounters an operator surrounded by two values within
parentheses, it leaves the result on the value stack.
( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
as if the original input were:
( 1 + ( 5 * ( 4 * 5 ) ) )
Repeating the argument:
( 1 + ( 5 * 20 ) )
( 1 + 100 )
101
Extensions. More ops, precedence order, associativity.
63
Page 64 · Stack-based programming languagesOriginal page 64 · Open the image for full detail.
Stack-based programming languages
Observation 1. Dijkstra's two-stack algorithm computes the same value if the
operator occurs after the two values.
( 1 ( ( 2 3 + ) ( 4 5 * ) * ) + )
Observation 2. All of the parentheses are redundant!
1 2 3 + 4 5 * * + Jan Lukasiewicz
Bottom line. Postfix or "reverse Polish" notation.
Applications. Postscript, Forth, calculators, Java virtual machine, …
64