目录:
1.队列的特点
队列(queue)是只允许在表的一端进行插入,在另一端进行删除的线性表。允许插入的一端叫做队尾(rear),允许删除的一端叫做队头(front)。
与栈相反,对列就是对日常生活中“先进先出”这一现象的抽象,能够帮助我们将许多实际问题抽象成数据结构来解决问题,比如操作系统对作业的排队。
2.队列的抽象数据类型(ADT)
将队列的操作抽象为一个接口,规范后续的顺序队列与链队列,包括队列空的判定、队列的长度、取队头元素、入队列、出队列以及队列的遍历。
public interface MyQueue {
public void clearQueue();
public boolean isEmpty();
public int queueLength();
public ElemType getHead();
public void enQueue(ElemType e) throws Exception;
public void deQueue() throws Exception;
public void traverse();
}
备注:队列的数据元素,以及链队列的结点如图:
详细代码请见
3.顺序队列的实现
3.1顺序队列的存储结构与初始化
与顺序表一样,顺序队列用规定大小的ElemType数组来存储数据,存取时,以队列头front 队列尾rear 为下标进行操作。
但这样的队列结构存在一定的问题,当出现队列尾部最后一个位置被占用时,新元素无法入队列,但此时,队列可能还未满,形成“假溢出”
要解决这个问题,比较好的方法是将顺序队列变成一个环状的空间,也即“循环队列”。头、尾指针以及队列元素之间的关系不变,只是在循环队列中,头尾在插入删除时的自增过程要在取一次模,这样,头尾就能在顺序空间内实现头尾相接,循环移动。
但现在又有了新的问题,在大小为maxSize的环状空间上,以下两种情况:
- 队空
- 队满
都会导致 front == rear,这样我们便无法通过头尾指针是否相同来判断出队列的实际情况。
解决办法也很简单,有两种思路:
- 少用一个元素空间,即队列空间为 maxSize 时,有 maxSize-1 个元素时就认为队满,不能再插入元素。这样,判断条件不变。
- 另设一个标记位区别队列是“空”还是“满”。
本文将采用第一种思路设计队列。
初始化时,调用含参构造方法,初始化以maxSize为最大长度的顺序队列。
public class SqQueue implements MyQueue {
private ElemType[] sqQueue;
private int maxLength;
private int front=0;
private int rear=0;
public SqQueue(int maxSize){
sqQueue=new ElemType[maxSize+1];//
maxLength=maxSize;
}
/*队列相关方法见下文*/
}
3.2顺序队列的入队和出队
入队:
@Override
public void enQueue(ElemType e) throws Exception{
if((rear+1)%maxLength==front){
throw new Exception("顺序队列满了");
}
sqQueue[rear]=e;
rear=(rear+1)%maxLength;
}
出队:
@Override
public void deQueue() throws Exception{
if(rear==front){
throw new Exception("顺序已空");
}
front=(front+1)%maxLength;
}
3.3顺序队列的其他方法
队列空的判断、清空队列、队列内数据的长度以及队列的遍历代码如下:
@Override
public void clearQueue() {
front=rear;
}
@Override
public boolean isEmpty() {
return front==rear;
}
@Override
public int queueLength() {
int k=rear-front;
if(k>=0){
return k;
}
else{
return maxLength+k;
}
}
@Override
public ElemType getHead() {
return sqQueue[front];
}
@Override
public void traverse() {
for(int i=front;(i%maxLength)!=rear;i++){
System.out.print(sqQueue[i]+" ");
}
System.out.println();
}
4.链队列的实现
与链表一样,链队列用一个LinkNode结点来存储数据。一个链队显然需要分别指示队头和队尾才能确定,为了操作方便,给链队添加一个头结点,并令指针始终指向头结点。
链队列的操作即为链表操作的特殊情况,只是需要进一步修改头指针或者尾指针。(可以参考链表的Java实现)
public class LinkQueue implements MyQueue {
LinkNode linkQueue = new LinkNode();
@Override
public void clearQueue() {
linkQueue.setNext(null);
}
@Override
public boolean isEmpty() {
return linkQueue.getNext() == null;
}
@Override
public int queueLength() {
LinkNode p = linkQueue;
int i;
for (i = 0; p.getNext() != null; i++) {
p = p.getNext();
}
return i;
}
@Override
public ElemType getHead() {
return linkQueue.getNext().getE();
}
@Override
public void deQueue() throws Exception {
LinkNode p = linkQueue.getNext();
linkQueue.setNext(p.getNext());
}
@Override
public void traverse() {
System.out.print("当前链队列:");
LinkNode p = linkQueue.getNext();
System.out.print(p.getE().toString());
p = p.getNext();
while (p != null) {
System.out.print(" -> ");
System.out.print(p.getE().toString());
p = p.getNext();
}
System.out.println();
}
@Override
public void enQueue(ElemType e) throws Exception {
LinkNode p = linkQueue;
LinkNode x=new LinkNode(e);
while (p.getNext() != null) {
p = p.getNext();
}
p.setNext(x);
}
}
5.测试代码
class QueueTest{
public static void main(String[] args) {
ElemType elem1 = new ElemType("王", 1);
ElemType elem2 = new ElemType("张", 2);
ElemType elem3 = new ElemType("李", 3);
ElemType elem4 = new ElemType("李", 4);
ElemType elem5 = new ElemType("赵", 5);
System.out.println("/---创建顺序队列---/");
SqQueue sqQueue=new SqQueue(3);
System.out.println("是否为空:"+sqQueue.isEmpty());
System.out.println();
System.out.println("/---进队列---/");
try {
sqQueue.enQueue(elem1);
}
catch (Exception e){
System.out.println(e);
}
sqQueue.traverse();
try {
sqQueue.enQueue(elem2);
}
catch (Exception e){
System.out.println(e);
}
sqQueue.traverse();
System.out.println();
System.out.println("/---出队列---/");
try {
sqQueue.deQueue();
}
catch (Exception e){
System.out.println(e);
}
sqQueue.traverse();
System.out.println();
System.out.println("/---进队列过多---/");
try {
sqQueue.enQueue(elem3);
}
catch (Exception e){
System.out.println(e);
}
sqQueue.traverse();
try {
sqQueue.enQueue(elem4);
}
catch (Exception e){
System.out.println(e);
}
sqQueue.traverse();
try {
sqQueue.enQueue(elem4);
}
catch (Exception e){
System.out.println(e);
}
System.out.println();
System.out.println("/---清理队列---/");
sqQueue.clearQueue();
sqQueue.traverse();
System.out.println("/---创建链队列---/");
LinkQueue linkQueue=new LinkQueue();
System.out.println("是否为空:"+sqQueue.isEmpty());
System.out.println();
System.out.println("/---进队列---/");
try {
linkQueue.enQueue(elem1);
}
catch (Exception e){
System.out.println(e);
}
linkQueue.traverse();
try {
linkQueue.enQueue(elem2);
}
catch (Exception e){
System.out.println(e);
}
linkQueue.traverse();
System.out.println();
System.out.println("/---出队列---/");
try {
linkQueue.deQueue();
}
catch (Exception e){
System.out.println(e);
}
linkQueue.traverse();
System.out.println();
System.out.println("/---进队列过多---/");
try {
linkQueue.enQueue(elem3);
}
catch (Exception e){
System.out.println(e);
}
linkQueue.traverse();
try {
linkQueue.enQueue(elem4);
}
catch (Exception e){
System.out.println(e);
}
linkQueue.traverse();
try {
linkQueue.enQueue(elem4);
}
catch (Exception e){
System.out.println(e);
}
System.out.println();
System.out.println("/---清理队列---/");
linkQueue.clearQueue();
linkQueue.traverse();
}
}
部分结果: