Stack
Implement
Tradeoffs: Array VS Linkedlist
- Linkedlist
- Every operation takes constant time in the worst case.
- Uses extra time and space to deal with links.
- Array
- Every operation takes constant amortized time.
- Less wasted space.
1 2 3 4 5 6 7 8 9 10 11 12
| public class StackOfStrings{
StackOfStrings()
void push(String item)
String pop()
boolean isEmpty()
int size() }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class LinkedStackOfStrings{ private Node first = null;
private class Node{ String item; 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; first = first.next; return item; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class FixedCapacityStackOfStrings{ private String[] s; private int N = 0;
public FixedCapacityStackOfStrings(int capacity){ s = new String[capacity]; }
public boolean isEmpty(){ return N == 0; }
public void push(String item){ s[N++] = item; }
public String pop(){ return s[--N]; } }
|
Overflow and Underflow
- Underflow : throw exception if pop from an empty stack
- Overflow : use resizing array for array implementation
Null items: We allow null items to be inserted.
Loitering: Holding a reference to an object when it is no longer needed.
1 2 3 4 5
| public String pop(){ String item = s[--N]; s[N] = null; return item; }
|
Problem How to grow and shrink array
- push(): increse by 1 too enpensive O(N^2)
- pop() : decrese by 1 too expensive
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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; }
|
how to shrink array
First try
- push(): double size of array s[] when array is full
- pop():halve size of array[] when array is one-half full
Too expensive in worst case
- Consider push-pop-push-pop-… sequence when array is full.
- Each operation takes time proportional to N.
Efficient solution
- push(): double size of array s[] when array is full
- pop(): halve size of array s[] when array is one-quarter full
1 2 3 4 5 6 7
| 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.
Queue
1 2 3 4 5 6 7 8 9 10 11 12
| public class QueueOfStrings{
QueueOfStrings()
void enqueue(String item)
String dequeue()
boolean isEmpty()
int size() }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class LinkedQueueOfStrings{ private Node first,last;
private class Node{ String item Node next; }
public boolean isEmpty(){ return first == null; }
public void enqueue(String item){ Node oldlast = last; last = new Node(); last.item = item; last.next = null; if(isEmpty()) first = last; else oldlast.next = last; }
public String dequeue(){ String item = first.item; first = first.next; if(isEmpty()) last = null; return item; } }
|
Generics
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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 oldfirst = first; first = new Node; first.item = item; first.next = oldfirst; }
public Item pop(){ Item item = first .item; first = first.next; return item; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class FixedCapacityStackOf<Item>{ private Item[] s; private int N = 0;
public FixedCapacityStackOfStrings(int capacity){ s = new Item[capacity]; }
public boolean isEmpty(){ return N == 0; }
public void push(Item item){ s[N++] = item; }
public Item pop(){ return s[--N]; } }
|
1 2 3
| Stack<Integer> s = new Stack<Integer>(); s.push(17); int a = s.pop();
|
- Bottom Line(要旨,基本论点,底价): Client code can use generic stack for any type of data.
Iterators
- Iterable has s method that returns an Iterator.
1 2 3
| public interface Iterable<Item>{ Iterator<Item> iterator(); }
|
- Iterator has methods hasNext() and next()
1 2 3 4 5
| public interface Iterator<Item>{ boolean hasNext(); Item next(); void remove(); }
|
- Iterable :Java supports elegant client code.
- foreach statement(shorthand)
- equivalent code(longhand)
1 2 3
| for(String s:stack){ StdOut.println(s) }
|
1 2 3 4 5
| Iterator<String> i = stack.iterator(); while(i.hasNext()){ String s = i.next(); StdOut.println(s); }
|
Stack iterator:linked-list implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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 = null;
public boolean hasNext(){ return current != null; }
public void remove(){ }
public Item next(){ Item item = current.item; current = current.next; return item; } } }
|
Stack iterator:array implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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(){ }
public Item next(){ return s[--i]; } }
|
Bag
- Main application. Adding items to a collection and iterating (when oeder doesn’t matter)
- Implementation. Stack(without pop) or queue(without dequeue)
1 2 3 4 5 6 7 8 9 10
| public class Bag<Item> implements Iterable<Item>{ Bag() void add(Item x)
int size()
Iterable<Item> iterator()
}
|
Application
未完待续…