队列Queue
队列Queue
1. 队列介绍
- 队列是一个有序列表,可以用数组或者链表来实现,用数组实现就是顺序存储,用链表实现就是链式存储。二者之间的区别是:
- 链式存储结构的内存地址不一定是连续的,而顺序存储结构的内存地址一定是连续的;
- 链式存储适用于频繁地进行插入、删除以及更新元素时的情况,而顺序存储适用于频繁地查询时使用;
- 顺序存储比链式存储更节约空间,因为链式存储结构每一个节点都有一个指针存储域;
- 顺序存储支持随机存取,方便操作;
- 但顺序存储再插入时花费更大的空间复杂度,而链式存储是索引后直接插入,因此在插入与删除时,链式存储比顺序存储更加方便、快捷。
- 队列遵循先入先出的原则。
2. 数组模拟队列

- 如上图是数组实现队列的声明,MaxSize表示该队列的最大容量,由于队列的输入与输出分别是从前后端处理,因此需要定义front和rear两个变量,分别记录队列前后端的下标,其中插入数据时front不变rear变化,输出数据时rear不变font变化(先进先出原则)。
- front指向队列头的前一个位置,rear指向队列最后一个元素所在的位置。
- 以数据进队列为参考,addQueue主要执行两个步骤:
- 先判断队列容量是否已满,只有当rear小于MaxSize-1(front和rear在初始定义时均为-1,数组下标从0开始)时才能将数据存入队列,rear==MaxSize-1时队列满无法存入数据;(数据出队列时则要判断队列是否为空,front==rear)
- 数据进队列时将尾指针后移rear++。
3. 数组模拟队列代码实现
package com.datastructure;
import java.util.Scanner;
/**
* @author SnkrGao
* @create 2023-03-01 21:51
*/
public class ArrayQueueDemo { // 数组模拟队列
public static void main(String args[]) {
// 创建队列
ArrayQueue queue = new ArrayQueue(5);
// 写一个程序用于测试
char key = ' '; // 接受用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("使用说明:");
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列中取数据");
System.out.println("h(head):查看队列头的数据");
System.out.println("-----------------------------");
key = scanner.next().charAt(0);
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("请输入数据:");
int n = scanner.nextInt();
queue.addQueue(n);
break;
case 'g':
try {
int res = queue.getQueue();
System.out.printf("取出的数据是:%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = queue.headQueue();
System.out.printf("队列头数据是:%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
}
}
// 队列构造类
class ArrayQueue {
private int maxSize; // 定义队列的最大容量
private int front; // 队列头
private int rear; // 队列尾
private int[] arr; // 用来模拟队列的数组,存放数据
// 构造函数,创建队列
public ArrayQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
this.front = -1; // 指向队列头的前一个位置
this.rear = -1; // 指向队列尾部数据的位置
this.arr = new int[arrMaxSize];
}
// 判断队列是否已满
public boolean isFull() {
return rear == maxSize - 1;
}
// 判断队列是否为空
public boolean isEmpty() {
return front == rear;
}
// 往队列中添加数据
public void addQueue(int n) {
// 首先判断队列是否已满
if (this.isFull()) {
System.out.println("队列已满,无法添加数据!");
return;
}
rear++; // rear后移
arr[rear] = n;
}
// 获取队列数据
public int getQueue() {
// 首先判断队列是否为空
if (this.isEmpty()) {
// 通过异常抛出 null
// 方法需要返回一个int类型参数,但不能直接return -1
throw new RuntimeException("队列为空,无法取数据!");
}
front++; // front后移,指向新的队列头的前一个位置
int result = arr[front]; // 从数组中取出的值
arr[front] = 0; // 数组中原位置赋0
return result;
}
// 显示队列的所有数据,此处显示的为所有在数组中存的数据,仅用于验证addQueue,不会随getQueue变化
public void showQueue() {
if (this.isEmpty()) {
System.out.println("队列为空,无法显示数据!");
return;
}
// 遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
// 显示队列头,而非取数据,用于验证getQueue
public int headQueue() {
if (this.isEmpty()) {
throw new RuntimeException("队列为空,无法获取队列头!");
}
// 显示
return arr[front+1];
}
}
4. 此数组模拟队列存在的问题及优化方法
- 目前设计的用数组模拟的队列只能使用一次,在上例中体现为队列中数据全部取出后,front=rear=Maxsize-1,无法再存入和取出数据(因为数组只能使用一次),没有达到复用的效果。
- 可以将此数组用算法进行改进,改成一个环形队列,取模%;或者也可以用两个栈来模拟队列(leetcode题)。
5. 数组模拟环形队列

一种实现思路:1. front变量进行调整:初始值改为front=0,改为就指向队列的第一个元素,即arr[front]就是队列的第一个元素;
- rear变量进行调整:初始值改为rear=0,改为指向队列最后一个元素的后一个位置,数组空出一个空间方便判断队列是否满或空(实现环形),即arr[rear-1]才是队列的最后一个元素,且数组大小为MaxSize队列最大长度为MaxSize-1;
- 判断队列满的条件:(rear + 1) % MaxSize == front;
- 判断队列空的条件:rear == front;
- 当数据入队列时,rear需后移的同时考虑取模(环形),即rear = (rear +1) % MaxSize;出队列时同理,front = (front + 1) % MaxSize;(因为front和rear都是自增的但队列是环形的,可能出现MaxSize=5但rear自增到6的情况,因此需要进行取模运算);
- 队列中的有效数据个数为:(rear + MaxSize - front) % MaxSize。
6. 数组模拟环形队列代码实现
package com.datastructure;
import java.util.Scanner;
/**
* @author SnkrGao
* @create 2023-03-03 20:38
*/
public class circleArrayQueueDemo { // 数组模拟环形队列
public static void main(String args[]) {
// 创建队列,队列实际容量为4
circleArrayQueue circlearrayqueue = new circleArrayQueue(5);
// 写一个程序用于测试
char key = ' ';
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("使用说明:");
System.out.println("s(show):显示环形队列");
System.out.println("e(exit):退出程序");
System.out.println("a(add):将数据添加进环形队列");
System.out.println("g(get):将数据从环形队列取出");
System.out.println("h(head):查看环形队列头的数据");
System.out.println("--------------------------------");
key = scanner.next().charAt(0);
switch (key) {
case 's' :
circlearrayqueue.showCircleQueue();
break;
case 'a' :
System.out.println("请输入数据:");
int value = scanner.nextInt();
circlearrayqueue.addCircleQueue(value);
break;
case 'g' :
try {
int result = circlearrayqueue.getCircleQueue();
System.out.println("从环形队列中取出的数据为:"+result);
} catch (Exception e) { // 输出捕获的异常信息
System.out.println(e.getMessage());
}
break;
case 'h' :
try {
int result = circlearrayqueue.headCircleQueue();
System.out.println("环形队列的头数据为:"+result);
} catch (Exception e) { // 输出捕获的异常信息
System.out.println(e.getMessage());
}
break;
case 'e' :
scanner.close();
loop = false;
System.out.println("程序进程结束,退出程序!");
break;
default :
break;
}
}
}
}
// 环形队列构造类
class circleArrayQueue {
private int maxSize; // 定义数组的最大容量,队列的最大容量为maxSize-1
private int front; // 指向队列头
private int rear; // 指向队列最后一个元素的后一个位置
private int[] arr;
// 构造方法,创建环形队列
public circleArrayQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
this.arr = new int[arrMaxSize];
// front和rear初始化时均为0,因此此处不需要重复对其赋值
}
// 判断环形队列是否为空
public boolean isEmpty() {
return front == rear;
}
// 判断环形队列是否已满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
// 往队列中添加数据
public void addCircleQueue(int n) {
if (isFull()) {
System.out.println("环形队列已满,无法添加数据!");
return;
}
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
// 从队列中取数据
public int getCircleQueue() {
if (isEmpty()) {
throw new RuntimeException("环形队列为空,无法取数据!");
}
// 先定义一个临时变量value用于存储值
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
// 显示队列中的所有数据
public void showCircleQueue() {
if(isEmpty()) {
System.out.println("环形队列为空,无法显示数据!");
return;
}
// 遍历队列应从front开始,遍历长度为队列有效数据个数
// size为环形队列的有效数据个数
int size = getCircleQueueSize();
for (int i = front; i < front+size; i++) {
// 由于front+size可能数组越界,因此需要取模
System.out.println("arr["+i%maxSize+"]="+arr[i%maxSize]);
}
}
// 求出队列的有效数据个数
public int getCircleQueueSize() {
return (rear + maxSize - front) % maxSize;
}
// 显示队列头的数据
public int headCircleQueue() {
if (isEmpty()) {
throw new RuntimeException("环形队列为空,无法获取环形队列头数据!");
}
return arr[front];
}
}

浙公网安备 33010602011771号