队列
1. 介绍
- 队列是一个有序列表,可以用数组或者链表来实现;
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
- 示意图:(使用数组模拟队列示意图)
2. 数组模拟队列思路
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列的声明如上示意图,其中maxSize是该队的最大容量;
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front 会随着数据输出而改变,而rear则是随着数据的输入而改变。
- 当我们将数据存入队列时成为”addQueue",addQueue 的处理需要有两个步骤:
- 将尾指针往后移:rear + 1,当front==rear时,队列为空;
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。rear == maxSize-1,队列满。
代码实现:
package com.lele.queue;
import java.util.Scanner;
/**
* author: hwl
* date: 2020/9/15 21:31
* version: 1.0.0
* modified by:
* description: 采用数组模拟队列
*/
public class ArrayQueueDemo {
public static void main(String[] args) {
// 创建一个队列
ArrayQueue queue = new ArrayQueue(3);
char key = ' '; // 接受用户的输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
// 输出一个菜单
while(loop) {
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列中取出数据");
System.out.println("s(show):显示队列中的数据");
System.out.println("h(head):查看队头的数据");
System.out.println("e(exit):退出程序");
key = scanner.next().charAt(0); // 接受一个字符
switch (key) {
case's':
queue.showQueue();
break;
case'a':
System.out.println("输入一个数");
int value = scanner.nextInt();
queue.addQueue(value);
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) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; // 队列头部指针,指向队列头部元素的前一个位置
rear = -1; // 队列尾部指针,指向队列尾部的最后一个元素
}
// 判断队列是否满了
public boolean isFull() {
return rear == maxSize - 1;
}
// 判断队列是否为空
public boolean isEmpty() {
return front == rear;
}
// 添加数据到队列,入队
public void addQueue(int n) {
if(isFull()) {
System.out.println("队列已满,不能加入数据。");
return;
}
rear++; // 尾部指针后移
arr[rear] = n;
}
// 获取队列的数据,出队
public int getQueue() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能取数据。");
}
front++; // 头部指针后移
return arr[front];
}
// 显示队列所有数据
public void showQueue() {
if (isEmpty()) {
System.out.println("队列为空,不能取数据");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("a[%d]=[%d]\n", i, arr[i]);
}
}
// 显示队列的头部数据,注意此处不是取出数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列为空,没有数据");
}
return arr[front+1];
}
}
3. 数组模拟环形队列
由于第二步中的数组使用一次就不能用了,不能达到复用的效果,于是将这个数组改进成一个环形队列(取模:%)
分析如下:
- front 变量含义做了调整:front 指向队列第一个元素,即 arr[front] 表示队列的第一个元素,front 的初始值等于0;
- rear变量含义做了调整:rear 指向队列的最后一个元素的后一个位置,目的是为了空出一个位置留作约定,rear 的初始值等于0;
- 当队列满时,条件是 (rear + 1) % maxSize = front ;
- 队列为空的判断条件为: rear == front;
- 队列中的有效数据的个数为: (rear + maxSize, front) % maxSize;
代码实现
package com.lele.queue;
import javafx.scene.shape.Circle;
import java.util.Scanner;
/**
* author: hwl
* date: 2020/9/17 20:17
* version: 1.0.0
* modified by:
* description: 数组模拟环形队列
*/
public class CircleArrayQueueDemo {
public static void main(String[] args) {
System.out.println("测试数组模拟环形队列的案例~~");
// 创建一个环形队列
CircleArray queue = new CircleArray(4); // 说明设置4,其队列的有效数据最大是3
char key = ' '; // 接受用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
// 输出一个菜单
while(loop) {
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):查看队列头的数据");
key = scanner.next().charAt(0); // 接受一个字符
switch(key) {
case's':
queue.showQueue();
break;
case'a':
System.out.println("输入一个数");
int value = scanner.nextInt();
queue.addQueue(value);
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;
}
}
System.out.println("程序退出~~");
}
}
class CircleArray {
private int maxSize; // 数组最大容量
private int front; // front 变量含义做了调整:front 指向队列第一个元素,即 arr[front] 表示队列的第一个元素,front 的初始值等于0;
private int rear; // rear变量含义做了调整:rear 指向队列的最后一个元素的后一个位置,目的是为了空出一个位置留作约定,rear 的初始值等于0;
private int[] arr; // 数组用来存放数据,模拟队列
// 创建队列构造器
public CircleArray(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
}
// 判断队列是否满了
public boolean isFull() {
return (rear+1) % maxSize == front;
}
// 判断队列是否为空
public boolean isEmpty() {
return front == rear;
}
// 添加数据到队列,入队
public void addQueue(int n) {
if(isFull()) {
System.out.println("队列已满,不能加入数据。");
return;
}
arr[rear] = n; //直接将数据加入队列
rear = (rear+1) % maxSize; // 将rear后移,这里必须考虑取模
}
// 获取队列的数据,出队
public int getQueue() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能取数据。");
}
// 先把front对应的值保留到一个临时变量
// 将 front 后移,考虑取模
// 将临时保存的变量返回
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
// 显示队列所有数据
public void showQueue() {
if (isEmpty()) {
System.out.println("队列为空,不能取数据");
return;
}
for (int i = 0; i < front + size(); i++) {
System.out.printf("a[%d]=[%d]\n", i % maxSize, arr[i%maxSize]);
}
}
// 求出当前队列有效数据的个数
public int size() {
return (rear + maxSize - front) % maxSize;
}
// 显示队列的头部数据,注意此处不是取出数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列为空,没有数据");
}
return arr[front];
}
}