下压栈(LIFO)
算法1.1 可动态调整数组大小的实现
public class ResizingArrayStack<Item> implements Iterable<Item> {
private Item[] a = (Item []) new Object[1];//元素栈
private int N;//元素个数
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
private void resize(int max) {
Item[] temp = (Item []) new Object[max];
for(int i = 0; i < max; i++) {
temp[i] = a[i];
}
a = temp;
}//调整数组大小
public void push(Item item) {
if(N == a.length) resize(2*a.length);
a[N++] = item;
}//添加元素到栈顶
public Item pop() {
Item item = a[--N];
a[N] = null;//防止对象游离
if(N > 0 && N == a.length/4) resize(a.length/2);
return item;
}//从栈顶删除元素
@Override
public Iterator<Item> iterator() {
return new ReverseArrayIterator;
}
private class ReverseArrayIterator implements Iterator<Item> {
private i = N;
@Override
public boolean hasNext() {
return i > 0;
}
@Override
public Item next() {
return a[--i];
}
@Override
public void remove() {}
}
}
要点
- 可变数组下压栈的主类和内部类需要分别实现Iterable,Iterator接口,从而实现LIFO逆序迭代,否则会按照数组FIFO迭代;
- algs4书中对于remove()总为空,避免在迭代中穿插修改数据结构的操作
- 为保证数组缩减后有一半的空间利用率,判断条件应该是N == a.length/4
- 删除栈顶元素时,实际上被弹出元素的引用仍保存在数组中,但该元素永远不会被访问,直到下次push()后,原有引用指向新元素.Java垃圾收集器才会回收空间,期间造成空间浪费,使用a[N] = null避免元素游离.
特点
- 算法1.1的每项操作用时与集合大小无关:操作始终只针对栈顶元素;
- 空间需求总是不超过集合大小乘一个常数;
- 栈永远不会溢出,使用率也不会低于四分之一,除非栈为空时,数组大小为1;
算法1.2 链表实现
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import java.util.Iterator;
public class Stack<Item> implements Iterable<Item> {
private Node first;
private int N;
private class Node {
Item item;
Node next;
}
public boolean isEmpty(){ return N == 0;}
public int size(){ return N; }
public void push(Item item){
Node oldFirst = first ;
first = new Node();
first.item = item;
first.next = oldFirst;
N++;
}
public Item pop(){
Item item;
item = first.item;
first = first.next;
N--;
return item;
}
@Override
public Iterator<Item> iterator(){
return new ListIterator();
}
private class ListIterator implements Iterator<Item>{
private Node current = first;
@Override
public boolean hasNext(){ return current != null;}
@Override
public Item next(){
Item item = current.item;
current = current.next;
return item;
}
@Override
public void remove(){}
}
}
要点
- 实例变量:头结点first
- 完成内部类Node;
- isEmpty()可以判断N == 0或first == null;
- 迭代实现同上
特点
- 可以处理任意类型数据;
- 所需空间总是和集合大小成正比;
- 操作所需时间和集合大小无关(链式结构);
关于内部类实现泛型接口*
算法内部类
private class ListIterator implements Iterator<Item>{}
//不是private class ListIterator<Item> implements Iterator<Item>
算法外部类
public class Stack<Item> implements Iterable<Item>
当类实现泛型接口时,不能确定接口中的泛型,这时就要求这个类也必须定义泛型,而且泛型名称要一致,因此主类Stack后也要定义泛型;当内部类实现和主类泛型类型一致的接口(本算法中),此时泛型已经可以由主类的定义判断,则内部类的泛型无需在定义泛型,如果重复定义会提示错误.