参考资料:《大话数据结构》
定义
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。允许插入的一端称为队头尾,允许删除的一端称为队头。简单的说,就是我们生活中排队买票,队头的人买完离开(删除),新来的人在队尾排队买票(插入)。
循环队列
先来定义两个指针,一个是队头指针(front),指向队头元素,一个是队尾指针(rear),指向队尾元素的下一个位置。当队列为空时,front等于rear(刚开始下标均为0)。当一个元素出队时,元素先出队,队头指针再往后移一位,当有新元素入队时,先入队,然后队尾指针再向后移一位。然而,当队列不是循环队列时,这是有问题的。假设现在数组长度为5,rear在数组下标为4的位置,由于之前有元素出队,front现在再下标为2的位置(即下标为0和1的地方都是空的)。现在有一新元素要入队,那么新元素在下标为4的地方,之后rear后移一位,但是数组长度为5,rear此时没法往后移,但此时下标为0和1的地方明明是空的,却不能存入元素。
故有了循环队列。上述问题中,我们可以让rear指向下标为0的位置。我们把队列头尾相接的顺序储存结构称为循环队列。
然而问题又来了,现在我们再往队列中加入两个新元素,则front和rear应该重合,同时指向下标为2的位置。
根据上面的定义,当front等于rear时,队列为空,但是此时队列并不为空。所以应该修改一下,规定队列为空的定义依然不变,当队列满时,数组还剩一个空闲单元。此时front和rear相邻,但是谁大谁小不好说(如下图)。我们发现,当数组长度为len时,满足front =(rear + 1) % len时,队列为满。如下图,front=0,rear=4,len=5,0=(4+1)%5。
此外,还有一个计算队列长度QueueSize的公式:QueueSize = (rear - front + len ) % len
由上面分析可得:
- 定义一个循环队列需要一个数组、front和rear(front和rear初始化为0)。
int queue = new int[len]; //len为数组长度
int front = 0;
int rear = 0;
2.根据上面公式判断队列是否满
public boolean isFull(){
if ((rear + 1) % len == front) //队列满时,队中还有一个空位
return true;
else
return false;
}
- 判断队列是否为空
public boolean isEmpty(){
if(front == rear)
return true;
else
return false;
}
4.根据上面公式求队列长度
public int length(){
return (rear - front + len) % len;
}
- 入队,先判断队列是否满,满则不能再入队。
public boolean enQueue(int val){
if(isFull())
return false;
arr[rear] = val; //队尾指针始终指向最后一个元素的下一个元素
rear = (rear + 1) % len; //队尾指针后移一位
return true;
}
- 出队,先判断队列是否为空,空则不能出队。
public boolean deQueue(){
if(isEmpty())
return false;
val = arr[front];
System.out.println(val);
front = (front + 1) % len; //队头指针始终指向第一个元素
return true;
}
队列的链式存储结构
队列的链式存储结构,就是单链表,但是只能头出尾进。我们将队头指针指向头结点,队尾指针指向终端结点。
当队列为空时,front和rear均指向头结点。
队列的入队操作相当于尾插法。
队列的出队操作就是把头结点的后面的结点出队,将头结点的后面结点改为出队结点的后面结点。
Java代码:
public class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
}
}
---------------------------------------------------------------------------------
public class Queue {
Node front; //队头指针
Node rear; //队尾指针
Node head; //链表的头结点
int val; //接收出队元素的值
/*初始化队列*/
public Queue() {
head = new Node(0);
front = head; //刚开始队头队尾指针均指向链表的头结点,头结点的值默认为0
rear = head;
}
/*判断队是否为空*/
public boolean isEmpty(){
if(front.next == null)
return true;
else
return false;
}
/*入队(队尾入)*/
public boolean enQueue(int val){
/*链表不存在满的情况*/
Node j = new Node(val);
j.next = null;
rear.next = j;
rear = j; //队尾指针rear始终指向队尾最后一个结点
return true;
}
/*出队(队头出)*/
public boolean deQueue() throws EmptyException {
if(isEmpty())
return false;
front = front.next; //此次出队后,队头指针指向刚刚出队的结点,下一次再出队时,队头指针先下移一位
val = front.data;
System.out.println(val);
return true;
}
/*求队列长度*/
public int length(){
int len = 0;
Node n = front;
while (n.next != null){
len++;
n = n.next;
}
return len;
}
}