我们可以将队列看成小朋友排队打饭:
- 有小朋友到指定的地点了-->出队
- 有新的小朋友加入了-->入队
- 相对于栈而言,队列的特性是:先进先出
- 先排队的小朋友肯定能先打到饭!
栈和队列的比较:
栈(Stack)和队列(Queue)是两种操作受限的线性表。这种受限表现在:栈的插入和删除操作只允许在表的尾端进行(在栈中成为“栈顶”),满足“First In Last Out”;队列只允许在表尾插入数据元素,在表头删除数据元素,满足“First In First Out”。它们都可以通过顺序结构和链式结构实现,插入与删除的时间复杂度都是O(1),在空间复杂度上两者也一样。
队列的应用场景:
- 计算机系统中各种资源的管理
- 消息缓冲器的管理
- 广度优先搜索遍历
顺序队列的实现:
顺序队列的实现也可以使用数组来完成,同栈的实现一样,只是栈是在同一端进行压栈和进栈操作,而队列是在一端做push,另一端做pop操作。
我们在实现顺序栈时使用头指针“front”和尾指针“rear”分别进行出队和入队操作,但普通的队列如上图所示,会发生“假溢出”现象,所以我们通常将数组弄成一个环状,即队头和队尾相连,这样就形成了“循环队列”,同时也解决了“假溢出”现象。循环队列是改进版的顺序队列。
对于普通队列的push或pop我们只需要对尾指针或头指针进行自增操作即可,但是循环队列我们就不能单纯的进行自增,当front或rear=maxSize-1时我们就不能进行自增操作了,比如一个队列尾长度为4的数组datas[4],那么当front或rear需要在0,1,2,3之间进行循环“推进”,以此达到循环队列的效果。所以我们可以使用rear = (rear+1)%maxSize ;front = (front+1)%maxSize ;公式进行指针计算。
需要注意的是,队空状态的条件为:front = rear。而如果整个队列全部存满数据那么,队满的条件也是front = rear,所以循环队列需要损失一个存储空间,如下图:
顺序队列的基本操作实现:
- 判断队列是否为空
- 判断队列是否满了
- 入队
- 出队
- 遍历
package com.hjy.datastructure.queue;
/**
* 顺序队列的基本操作(数组实现)
*
* @param <T>
*/
public class ArrayQueue<T> {
private T[] elementData; // 使用数组作为队列的容器
private int maxSize; // 队列的容量
private int front; // 头指针
private int rear; // 尾指针
/**
* 初始化队列
*
* @param maxSize
*/
public ArrayQueue(int maxSize) {
if (maxSize < 1) {
maxSize = 1;
}
this.maxSize = maxSize;
this.front = 0;
this.rear = 0;
this.elementData = (T[]) new Object[this.maxSize];
}
/**
* 判断队列是否为空
*
* @return
*/
public boolean isEmpty() {
if (front == rear) {
return true;
} else {
return false;
}
}
/**
* 判断队列是否满了
*
* @return
*/
public boolean isFull() {
if ((rear + 1) % this.maxSize == front) {
return true;
} else {
return false;
}
}
/**
* 入队
*
* @param e
* @return
*/
public T enQueue(T e) {
if (isFull()) {
System.out.println("队列已经满了");
return null;
} else {
elementData[rear] = e; // 进队
rear = (rear + 1) % maxSize; // 队尾指针+1.
return e;
}
}
/**
* 出队
*
* @return
*/
public T deQueue() {
if (isEmpty()) {
System.out.println("队列是空的");
return null;
} else {
T e = elementData[front]; // 出队
front = (front + 1) % maxSize; // 队头指针+1
return e;
}
}
/**
* 遍历队列
*/
public void traverse() {
int i = front;
System.out.println("遍历队列:");
while (i != rear) {
System.out.print(elementData[i] + " ");
i = (i + 1) % maxSize;
}
System.out.println();
}
public static void main(String[] args) {
ArrayQueue q = new ArrayQueue(5);
// 判断是否为空
System.out.println("是否为空:" + q.isEmpty());
// 入队
System.out.println("入队:" + q.enQueue(1));
System.out.println("入队:" + q.enQueue(2));
System.out.println("入队:" + q.enQueue(3));
System.out.println("入队:" + q.enQueue(4));
// 判断队列是否满了
System.out.println("是否满了:" + q.isFull());
// 遍历队列
q.traverse();
// 出队
System.out.println("出队:" + q.deQueue());
System.out.println("出队:" + q.deQueue());
// 遍历
q.traverse();
// 继续入队
System.out.println("入队:" + q.enQueue(5));
System.out.println("入队:" + q.enQueue(6));
System.out.println("入队:" + q.enQueue(7));
// 遍历
q.traverse();
}
}
输出结果:
是否为空:true
入队:1
入队:2
入队:3
入队:4
是否满了:true
遍历队列:
1 2 3 4
出队:1
出队:2
遍历队列:
3 4
入队:5
入队:6
队列已经满了
入队:null
遍历队列:
3 4 5 6
链式队列的基本操作实现:
- 判断队列是否为空
- 入队
- 出队
- 返回元素个数
- 遍历队列
package com.hjy.datastructure.list;
/**
* 定义一个单链表
*/
public class Node<T> {
// 存储的数据
public T value;
// 下一个节点的引用
public Node<T> next;
public Node(T value) {
this.value = value;
}
}
package com.hjy.datastructure.queue;
import com.hjy.datastructure.list.Node;
public class LinkedQueue<T> {
private Node<T> front; // 队头
private Node<T> rear; // 队尾
private int size; // 实际元素个数
/**
* 是否为空
* @return
*/
public boolean isEmpty() {
return (front == null && rear == null) ? true : false;
}
/**
* 入队
* @param value
*/
public T enQueue(T value) {
Node<T> node = new Node<>(value);
if (isEmpty()) {
front = rear = node;
} else {
rear.next = node;
rear = node;
}
size++;
return value;
}
/**
* 出队
* @return
*/
public T deQueue() {
if (isEmpty()) {
System.out.println("队列为空");
return null;
}
Node<T> node = front;
front = node.next;
size--;
if (size == 0) {
// 删除掉最后一个元素
rear = front = null;
}
return node.value;
}
/**
* 元素数量
* @return
*/
public int size() {
return this.size;
}
/**
* 遍历队列
*/
public void traverse() {
Node<T> node = front;
System.out.println("遍历队列:");
while (node != null) {
System.out.print(node.value + " ");
node = node.next;
}
System.out.println();
}
public static void main(String[] args) {
LinkedQueue q = new LinkedQueue();
// 判断是否为空
System.out.println("是否为空:" + q.isEmpty());
// 入队
System.out.println("入队:" + q.enQueue(1));
System.out.println("入队:" + q.enQueue(2));
System.out.println("入队:" + q.enQueue(3));
System.out.println("入队:" + q.enQueue(4));
// 遍历队列
q.traverse();
// 出队
System.out.println("出队:" + q.deQueue());
System.out.println("出队:" + q.deQueue());
// 遍历
q.traverse();
// 继续入队
System.out.println("入队:" + q.enQueue(5));
System.out.println("入队:" + q.enQueue(6));
System.out.println("入队:" + q.enQueue(7));
// 遍历
q.traverse();
System.out.println("元素数量:" + q.size());
}
}
输入结果:
是否为空:true
入队:1
入队:2
入队:3
入队:4
遍历队列:
1 2 3 4
出队:1
出队:2
遍历队列:
3 4
入队:5
入队:6
入队:7
遍历队列:
3 4 5 6 7
元素数量:5