API中均包含以下四项:
1.无参构造函数
2.添加单个元素
3.是否为空
4.返回集合大小
1 泛型
Stack<String> stack = new Stack<String>();
String处是一个类型占位符,可填其余类型
2 迭代
//foreach语句,打印整个集合
Quene<Transaction> collection = new Quene<Transaction>();
for(Transaction t:collection)
{
StdOut.println(t);
}
//与如下的while语句等价
Iterator<Transaction> i = collection.iterator();
while(i.hasNext())
{
Transaction s = i.next();
StdOut.println(s);
}
-
任意可迭代的集合数据类型中需要实现的东西:
1.实现一个Iterator()方法并返回一个Iterator对象;
2.Iterator类必须包含两个方法:hasNext()和next()(返回集合中的一个泛型元素) -
用接口机制来指定一个类必须实现的方法:
public interface Iterable<Item>
{
Iterator<Item> iterator();//这是一个Iterator()方法,返回一个迭代器Iterator<Item>
}
//迭代器是一个实现了hasNext()和next()方法的类的对象(c++中迭代器就是指针),由以下接口所定义(即java.util.Iterator)
public interface Iterator<Item>
{
boolean hasNext();
Item next();
void remove();
}
//一个例子:逆序迭代遍历数组
import java.util.Iterator;
private class ReverseArrayIterator implements Iterator<Item>
{
private int i = N;
public boolean hasNext(){return i>0;}
public Item next(){return a[--i];}
public void remove(){ }//remove()方法总为空,因为我们希望避免在迭代中穿插能够修改数据结构的操作。
}
3 Bag
不讲究顺序,目的是收集元素并迭代遍历所有收集到的元素。
public class Bag<Item> implements Iterable<Item>
Bag()
void add(Item item)
boolean isEmpty()
int size()
背包的典型用例:计算输入中的所有double值的平均值和样本标准差
Bag<Double> numbers = new Bag<Double>();
while(!StdIn.isEmpty())
numbers.add(StdIn.readDouble());
int N = numbers.size();
double sum = 0.0;
for(double x : numbers)
sum += x;
double mean = sum/N;
sum = 0.0;
for(double x : numbers)
sum += (x-mean)*(x-mean)
double std = Math.sqrt(sum/(N-1));
StdOut.printf(“Mean:%.2f\n”,mean);
StdOut.printf(“Std dev:%.2f\n”,std);
4 Quene
先进先出
public class Quene<Item> implements Iterable<Item>
Quene()
void enquene(Item item)
Item dequene() //删除最早添加的一个元素
boolean isEmpty()
int size()
队列的典型用例:无需预先知道文件的大小即可将文件中的所有整数读入一个数组
In in = new In(name);
Quene<Integer> q = new Quene<Integer>();
while(!in.isEmpty())
q.enquene(in.readInt());
int N = q.size();
int[] a = new int[N];
for(int i = 0;i<N;i++)
a[i] = q.dequeue()
return a;
5 Stack
后进先出
public class Stack<Item> implements Iterable<Item>
Stack()
void push(Item item)
Item pop() //删除最近添加的一个元素
boolean isEmpty()
int size()
栈的典型用例1:将输入中的所有整数逆序排列
Stack<Interger> stack = new Stack<Interger>();
while(!StdIn.isEmpty())
stack.push(StdIn.readInt());
for(int i : stack)
StdOut.println(i);
栈的典型用例2:Dijkstra的双栈算术表达式求值算法
1.将操作数压入操作数栈
2.将运算符压入运算符栈
3.忽略左括号
4.在遇到右括号时,弹出一个运算符,弹出所需数量的操作数,并将运算符和操作数的运算结果压入操作数栈
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(“*”)) ops.push(s);
else if(s.equals(“/”)) ops.push(s);
else if(s.equals(“sqrt”)) ops.push(s);
else if(s.equals(“)”))
{
String op = ops.pop();
double v = vals.pop();
if(op.equals(“+”)) v = vals.pop()+v;
else if(op.equals(“-”)) v = vals.pop()-v;
else if(op.equals(“*”)) v = vals.pop()*v;
else if(op.equals(“/“)) v = vals.pop()/v;
else if(op.equals(“sqrt”)) v = Math.sqrt(v);
vals.push(v);
}
else vals.push(Double.parseDouble(s));
}
StdOut.println(vals.pop());
5.1 定容栈
容量固定的字符串栈的抽象数据类型,只能处理String值,要求用例指定一个容量且不支持迭代。FixedCapacityOfStrings(int cap)
数据类型的实现
public class FixedCapacityStackOfStrings
{
private String[] a;
private int N;
public FixedCapacityOfStrings(int cap)
{ a = new String[cap]; }
public boolean isEmpty()
{ return N==0; }
public int size()
{ return N; }
public void push(String item)
{ a[N++] = item; }
public String pop()
{ return a[--N]; } //栈的顶部位于a[N-1]
}
测试用例:
输入:to be or not to - be - - that - - - is
输出:to be not that or be ( 2 left on stack )
FixedCapacityStackOfStrings s;
s = new FixedCapacityStackOfStrings(100);
while(!StdIn.isEmpty())
{
String item = StdIn.readString();
if(!item.equals(“-”))
s.push(item);
else if(!s.isEmpty())
StdOut.print(s.pop()+” “);
}
StdOut.println(“(“+s.size()+”left on stack)”);
5.2 实现一个泛型的定容栈
对比前文给出的实现代码
public class FixedCapacityStack<Item>
{ private Item[] a;
private int N;
public FixedCapacityOfStrings(int cap)
{ a = new Item[cap]; }//java不允许创建泛型数组,故这是错的
{ a = (Item[]) new Object[cap]; }//使用了类型转换
public boolean isEmpty()
{ return N==0; }
public int size()
{ return N; }
public void push(Item item)
{ a[N++] = item; }
public Item pop()
{ return a[--N]; } //栈的顶部位于a[N-1]
}
测试用例同上:
输入:to be or not to - be - - that - - - is
输出:to be not that or be ( 2 left on stack )
FixedCapacityStack<String> s;
s = new FixedCapacityStack<String>(100);
while(!StdIn.isEmpty())
{
String item = StdIn.readString();
if(!item.equals(“-”))
s.push(item);
else if(!s.isEmpty())
StdOut.print(s.pop()+” “);
}
StdOut.println(“(“+s.size()+”left on stack)”);
5.3 调整数组的大小
选择用数组表示栈内容必须预先估计栈的最大容量。
正常来说,push()方法需要在代码中检测栈是否已满,也应该含有一个isFull()方法检测栈是否已满。要从处理栈是否已满的问题中解脱出来用以下方法
//将栈移动到另一个大小不同的数组中
private void resize(int max)
{
Itemp[] temp = (Item[])new Object[max];
for(int i = 0;i<N;i++)
temp[i] = a[i]
a = temp;
}
//在push()中检查数组是否太小
public void push(Item item)
{
if(N == a.length)resize(2*a.length);
a[N++] = item;
}
//在pop()中删除栈定元素
public Item pop()
{
Item item = a[--N];
a[N] = null;//避免**对象游离**
if(N>0 && N == a.length/4)resize(a.length/2);
return item;
}
算法1.1 下压栈(能够动态调整数组大小的实现)
import java.util.Iterator;
public class ResizingArrayStack<Item> implements Iterable<Item>
{
private Item[] a = (Item())new Object[1];//栈元素
private int N = 0;//元素数量
public boolean isEmpty(){return N == 0;}
public int size(){return N;}
private void resize(int max)
{
//将栈移动到一个大小为max的数组
Item[] temp = (Item[])new Object[max];
for(int i = 0;i<N;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;
}
public Iterator<Item> iterator()
{ return new ReverseArrayIterator(); }
private class ReverseArrayIterator implements Iterator<Item>
{
private int i = N;
public boolean hasNext{return i>0;}
public Item next(){return a[--i];}
public void remove(){ }
}
}
这份泛型可迭代的Stack API的实现是所有集合类抽象数据类型实现的模版。它将所有元素保存在数组中,并动态调整数组的大小和栈大小之比小于一个常数。
缺点在于某些push和pop操作会调整数组的大小,这项操作的耗时和栈大小成正比。