队列是一种线性集合,其元素一端加入,从另一端删除,因此我们说队列元素是按先进先出(FIFO)方式处理。
队列的处理过程:通常队列会画成水平,其中一端作为队列的前端(front)也称队首(head),另一端作为队列的末端(rear)也称队尾(tail).元素都是从队列末端进入,从队列前端退出.
因而在队列中,其处理过程可在队列的两端进行,而在栈中,其处理过程只在栈的一端进行,但两者也有相似之处,与栈类似,队列中也没有操作能让用户“抵达”队列中部,同样也没有操作允许用户重组或删除多个元素。(不过这些操作都可以再链表中实现)
下面我们定义一个泛型QueueADT接口来表示队列的各种操作(队列的首要作用是保存顺序,而栈则相反是用来颠倒顺序)
package xidian.sl.queue; import xidian.sl.stack.EmptyCollectionException; /** * 栈的首要作用是颠倒顺序,而队列的首要作用是保持顺序 * */ public interface QueueADT<T> { /*向队列末尾添加一个元素*/ public void enqueue(T element); /*从队列前端删除一个元素*/ public T dequeue() throws EmptyCollectionException; /*考察队列前端的那个元素*/ public T first(); /*判断队列是否为空*/ public boolean isEmpty(); /*判定队列中的元素个数*/ public int size(); /*返回队列的字符串表示*/ public String toString(); }
与栈的实现一样,我们这里也提供两种实现方法:链表与数组的实现
1.链表实现队列:
队列与栈的区别在于,我们必须要操作链表的两端。因此,除了一个指向链表首元素的引用外,还需要跟踪另一个指向链表末元素的引用。再增加一个整形变量count来跟踪队列中的元素个数。
综合考虑,我们使用末端入列,前端出列
package xidian.sl.queue; import xidian.sl.stack.EmptyCollectionException; import xidian.sl.stack.LinearNode; public class LinkedQueue<T> implements QueueADT<T> { //跟踪队列中的元素个数 private int count; //指向首元素末元素的引用 private LinearNode<T> front, rear; public LinkedQueue(){ count = 0; front = rear = null; } /** * 实现dequeue操作时,确保至少存在一个可返回的元素,如果没有,就要抛出异常 * @throws EmptyCollectionException * */ public T dequeue() throws EmptyCollectionException { if(isEmpty()){ throw new EmptyCollectionException("queue"); } T result = front.getElement(); front = front.getNext(); count--; //如果此时队列为空,则要将rear引用设置为null,front也为null,但由于front设置为链表的next引用,已经有处理 if(isEmpty()){ rear = null; } return result; } /** * enqueue操作要求将新元素放到链表的末端 * 一般情况下,将当前某元素的next引用设置为指向这个新元素,并重新把rear引用设置为指向这个新添加的末元素,但是,如果队列 * 目前为空,则front引用也要设置为指向这个新元素 * */ public void enqueue(T element) { LinearNode<T> node = new LinearNode<T>(element); if(isEmpty()){ front = node; }else{ rear.setNext(node); } rear = node; count++; } @Override public T first() { T result = front.getElement(); return result; } @Override public boolean isEmpty() { return count == 0 ? true : false; } @Override public int size() { return count; } }
这里使用到的LinearNode类与在栈中使用到的是一样的:
package xidian.sl.stack; /** * 节点类,含有另个引用,一个指向链表的下一个LinearNode<T>节点, * 另一个指定本节点中存储的元素 * */ public class LinearNode<T> { /*指向下一个节点*/ private LinearNode<T> next; /*本节点存储的元素*/ private T element; /*创建一个空的节点*/ public LinearNode(){ next = null; element = null; } /*创建一个存储了特殊元素的节点*/ public LinearNode(T elem){ next = null; element = elem; } /*返回下一个节点*/ public LinearNode<T> getNext(){ return next; } /*设置下一个节点*/ public void setNext(LinearNode<T> node){ next = node; } /*获得当前节点存储的元素*/ public T getElement() { return element; } /*设置当前节点存储的元素*/ public void setElement(T element) { this.element = element; } }