3.8 队列的实现
基于链表数据结构实现Queue API也很简单,如下面算法所述。它将队列表示为一条从最早插入的元索到最近插入的元素的链表,实例变量first指向队列的开头,实例变量last指向队列的结尾。这样,要将一个元素 入列( enqueue(),我们就将它添加到表尾(但是在链表为空时需要将first和last都指向新结点);要将一个元素出列( dequeue(),我们就删除表头的结点(代码和Stack的pop()方法相同,只是当链表为空时需要更新last的值)。size()和isEmpty()方法的实现和Stack相同。和Stack一样,Queue 的实现也使用了泛型参数Item。Queue 的实现使用的数据结构和Stack相同一链表, 但它实现了不同的添加和删除元素的算法,这也是用例所看到的后进先出和先进后出的区别所在。和刚才一样,我们用链表达到了最优设计目标:它可以处理任意类型的数据:所需的空间总是和集合的大小成正比,操作所需的时间总是和集合的大小无关。
Queue的测试用例
public static void main(String[] args){// 创建一个队列并操作字符事入列成出列Queue<String> q = new Queue<String>();while (!StdIn.isEmpty()){String item = StdIn.readString();if(! item.equals("...")) q. dequeue(item);else if (!q.isEmpty()) Std0ut.print(q. dequeue() +”");}StdOut.println("("+ q.size() + " left on queue)");}
public class Queue<Item> implenents Iterable<Item>{private Node first; //指向最早添加的结点的链接private Node last; //指向最近添加的结点的链接private int N;//队列中的元素数量private class Node{//定义了结点的嵌套类Item item;Node next;}public boolean isEmpty() { return first == null; } //或: N==0public int size() { return N; } public void enqueue(Item item){//向表尾添加元素Node oldlast = last;last = new Node():last.item = item;if (isEmpty()) first = last;else oldlast.next = last;}public Item dequeue(){//从表头删除元素Item item=first.item;if (isEmpty()) last = null;return item;}}
这份泛型的Queue实现的基础是链表数据结构。它可以用于创建任意数据类型的队列。
在结构化存储数据集时,链表是数组的一种重要的替代方式。在现代编程语言中,安全指针、自动垃圾回收和抽象数据类型的使用使我们能够将链表处理的代码封装在若干个类中。
3.9 背包的实现
用链表数据结构实现我们的Bag API只需要将Stack中的push()改名为add(),并去掉popO的实现即可。对于Stack,链表的访问顺序是后进先出;对于Queue,链表的访问顺序是先进先出;对于Bag,它正好也是后进先出的顺序,但顺序在这里并不重要。要在集合数据类型中实现迭代,第一步就是要添加下面这行代码,这样我们的代码才能引用Java的Iterator接口:
import java. util.Iterator;
第二步是在类的声明中添加这行代码,它保证了类必然会提供一个iterator()方法:
implements Iterable<Item>
iterator()方法本身只是简单地从实现了lterator 接口的类中返回一个对象:
public Iterator<Item iterator>(){return new ListIterator(); }
这段代码保证了类必然会实现方法hasNext()、next(和remove()供用例的foreach语法使用。要实现这些方法,算法中的嵌套类ListIterator维护了一个实例变量current来记录链表的当前结点。hasNext()方法会检测current是否为null, next()方法会保存当前元素的引用,将current变量指向链表中的下个结点并返回所保存的引用。
import java.util.Iterator;public class Bag<Item> implements Iterator(){private Node first; //链表的首结点private class Node;}public void add(Item item){//和Stack的push()方法完全相同first = new Node();first.next = oldfirst;first.item=item;first.next=oldfirst;}public Iterator<Item iterator>(){return new ListIterator();}private class ListIterator implements Iterator<Item>{private Node current =first;public boolean hashNext(){return current != null;}public void remove() { }public Item next(){current = current.next;return item;}}
这份Bag的实现维护了一条链表, 用于保存所有通过add()添加的元素。size() 和isEmpty()方!法的代码和Stack中的完全相同。迭代器会遍历链表并将当前结点保存在current变量中。Stack和Queue的链表访问顺序分别是后进先出和先进先出而已。