Stacks and queues:
用来保存collections of objects,支持插入,移除,遍历操作,区别是stack是last in first out,queue是last in last out。
public class StackOfStrings
StackOfStrings() //create an empty stack
void push(String item) //insert a new string onto stack
String pop() //remove and return the string most recently added
boolean isEmpty() //is the stack empty?
int size() //number of strings on the stack
客户端:
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);
}
}
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;
}
}
每个操作都是constant time
内存占用:object overhead,inner class overhead,string,node,一共16+8+8+8=40 bytes
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; } //先将s[N]赋值为item,再将N增加1指向下一个entry
public String pop()
{ return s[--N]; } //先将N减一指向最近保存的entry
}
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.
public String pop()
{ return s[--N]; } //s[N]保存的object已经不需要了,但是这里还存在array里
public String pop()
{
String item = s[--N];
s[N] = null; //只有把指向不需要的object的reference去掉,才能释放内存
return item;
}
之前的实现需要用户在创建FixedCapacityStackOfStrings时提供capacity作为array的长度,但是大多数时候这是不现实的,所以需要实现能够自行变大和变小的array。
最直接简单的想法是:
・push(): increase size of array s[] by 1.
・pop(): decrease size of array s[] by 1.
但是这样每次push或pop操作就需要将array已有的内容复制到新array里。插入N个object,复杂度为:
1+2+3+...+N=(1+N)∗N/2 1 + 2 + 3 + . . . + N = ( 1 + N ) ∗ N / 2 ~ N22 N 2 2
另一种做法是,每次array满了之后,新建一个两倍于之前array大小的新array
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;
}
插入N个object,复杂度为:
N个object,需要新建array lg(N)次,并把旧array已有的内容复制到新array里,即
此外,当pop掉一些object之后,应该减小array大小,最简单的想法是,和增大array一倍一样,当array只有一半使用时就把只存减小到之前的一半:
・push(): double size of array s[] when array is full.
・pop(): halve size of array s[] when array is one-half full.
但是,考虑最坏情况下的复杂度:
当array满了之后,不断的push,pop,push,pop,此时每个操作都需要新建array,并将旧array的N个objects复制过来,每一次的array存取都是~N。
另一种高效率的方法是:
・push(): double size of array s[] when array is full.
・pop(): halve size of array s[] when array is one-quarter full.
public String pop()
{
String item = s[--N];
s[N] = null;
if (N > 0 && N == s.length/4) resize(s.length/2);
return item;
}
这种方法的复杂度分析:
/ | best | worst | amortized |
---|---|---|---|
construct | 1 | 1 | 1 |
push | 1 | N | 1 |
pop | 1 | N | 1 |
size | 1 | 1 | 1 |
内存占用:
8 bytes (reference to array)
24 bytes (array overhead)
8 bytes × array size
4 bytes (int)
4 bytes (padding)
当array size = N (全满)的时候,~8N
当array size = 4N (只占四分之一)的时候,~32N
使用Linked list和Resizing array实现的Stack比较:
Linked-list implementation.
・Every operation takes constant time in the worst case.
・Uses extra time and space to deal with the links.
Resizing-array implementation.
・Every operation takes constant amortized time.
・Less wasted space.
public class QueueOfStrings
QueueOfStrings() create an empty queue
void enqueue(String item) insert a new string onto queue
String dequeue() remove and return the string
least recently added
boolean isEmpty() is the queue empty?
int size() number of strings on the queue
queue是先入先出,因此enqueue时把刚存入的放在链表最后,dequeue时从链表前面开始取。
public class LinkedQueueOfStrings
{
private Node first, last;
private class Node
{ /* same as in StackOfStrings */ }
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;
}
}
・Use array q[] to store items in queue.
・enqueue(): add new item at q[tail].
・dequeue(): remove item from q[head].
・Update head and tail modulo the capacity.
・Add resizing array.
public class Stack-
{
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;
}
}
Java不允许创建generic array,所以下面这种实现是不可以的。
public class FixedCapacityStack-
{
private Item[] s;
private int N = 0;
public FixedCapacityStack(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]; }
}
在这一课里,采用了如下的使用强制类型转换的实现方法,后面应该会采用其他方法替代:
public class FixedCapacityStack-
{
private Item[] s;
private int N = 0;
public FixedCapacityStack(int capacity)
{ s = (Item[]) new Object[capacity]; }
public boolean isEmpty()
{ return N == 0; }
public void push(Item item)
{ s[N++] = item; }
public Item pop()
{ return s[--N]; }
}
对于primitive types,每一种primitive type都有wrapper object type,例如Integer 是int的wrapper type。
//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();
Iterable:Has a method that returns an Iterator.
Iterator:Has methods hasNext() and next().
Iterable interface
public interface Iterable-
{
Iterator
- iterator();
}
Iterator inferface
public interface Iterator-
{
boolean hasNext();
Item next();
void remove();
}
Client段可以这样遍历stack的内容:
for (String s : stack)
StdOut.println(s);
或等价地:
Iterator i = stack.iterator();
while (i.hasNext())
{
String s = i.next();
StdOut.println(s);
}
import java.util.Iterator;
public class Stack<Item> implements Iterable<Item>
{
...
public Iterator- iterator() { return new ListIterator(); }
private class ListIterator implements Iterator<Item>
{
private Node current = first;
public boolean hasNext() { return current != null; }
public void remove() { /* not supported */ }
public Item next()
{
Item item = current.item;
current = current.next;
return item;
}
}
}
import java.util.Iterator;
public class Stack<Item> implements Iterable<Item>
{
…
public Iterator- 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]; }
}
}
向一个collections里添加items,并能够进行遍历,顺序不重要,实现的时候相当于没有pop的stack或者没有dequeue的queue。
public class Bag- implements Iterable
-
Bag() create an empty bag
void add(Item x) insert a new item onto bag
int size() number of items in bag
Iterable
- iterator() iterator for all items in bag
・遇到Value: push onto the value stack.
・遇到Operator: push onto the operator stack.
・遇到Left parenthesis: ignore.
・遇到Right parenthesis: pop operator and two values; push the result of applying that operator to those values onto the value stack.
public class Evaluate
{
public static void main(String[] args)
{
Stack ops = new Stack();
Stack vals = new Stack();
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());
}
}