用Java实现数组模拟队列
本文共分为两部分:
- 用数组模拟队列
- 用数组模拟环形队列
队列
- 队列是一个有序列表,可以用数组或者链表来实现。
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。
- 示意图:使用数组模拟队列示意图
数组模拟队列思路
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如上图,其中maxSize是该队列的最大容量
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变,如上图所示。
- 当我们将数据存入队列是称为“addQueue”,addQueue是处理需要有以下两个步骤:
1).将尾指针往后移:rear+1,当front==rear,代表队列为空。
2).若尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所致的数组元素中,否则无法存入数据。rear == maxSize-1,代表队列满了。
代码如下:
package cn.datastructures.queue;
import java.util.Scanner;
/**
* 测试数组模拟队列
* @author 最皮双皮奶
*/
public class ArrayQueueDemo {
public static void main(String[] args) {
//创建队列
ArrayQueue queue = new ArrayQueue(5);
//创建一个值接收用户输入的操作数据
char key = ' ';
Scanner scanner = new Scanner(System.in);
boolean flag = true;
//输出一个菜单
while (flag) {
System.out.println("s:展示队列");
System.out.println("a:添加数据到队列");
System.out.println("g:取出队列中的数据");
System.out.println("h:查看队列头数据");
System.out.println("e:退出程序");
//key接收用户输入的字符
key = scanner.next().charAt(0);
//判断用户输入的数据,进行操作
switch (key) {
//展示数据
case 's':
try {
queue.showQueue();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//添加数据
case 'a':
try {
System.out.println("请输入一个数");
int i = scanner.nextInt();
queue.addData(i);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//获取数据
case 'g':
try {
int data = queue.getData();
System.out.println("数据为:" + data);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//展示头数据
case 'h':
try {
queue.headData();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//退出
case 'e':
flag = false;
break;
default:
break;
}
}
System.out.println("退出程序!!!");
}
}
/**
1. 用数组模拟队列(有缺陷)
*/
class ArrayQueue {
/**
表示队列前端的下标
*/
private int front;
/**
表示队列后端的下标
*/
private int rear;
/**
最大容量
*/
private int maxSize;
/**
用来模拟队列的数组
*/
private int[] arr;
/**
构造方法
*/
public ArrayQueue(int maxSize) {
this.maxSize = maxSize;
this.arr = new int[maxSize];
//指向队列头部,front是指向队列头的前一个数据。
this.front = -1;
//指向队列尾部,rear是指向队列尾的数据(队列最后一个数据)。
this.rear = -1;
}
/**
判断队列是否为空
*/
private boolean isEmpty() {
return front == rear;
}
/**
判断队列是否已满
*/
private boolean isFull() {
return rear == maxSize - 1;
}
/**
添加数据
*/
public void addData(int data) {
//判断队列是否已满
if (isFull()) {
throw new RuntimeException("队列已满,无法添加数据");
}
rear++;//rear后移一位
arr[rear] = data;
}
/**
取出数据
*/
public int getData() {
//判断队列是否为空
if (isEmpty()) {
//空的话抛出异常。
throw new RuntimeException("队列为空,无法获取数据");
}
front++;//front后移一位
return arr[front];
}
/**
展示数据
*/
public void showQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列为空,无法显示数据");
}
//front在取出数据时会向上移,front指向的是头元素的前一个下标,所以遍历要从front+1开始
for (int i = front + 1; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
/**
展示头数据(front位置的数据)
*/
public void headData() {
if (isEmpty()) {
throw new RuntimeException("队列为空,无法显示头数据");
}
System.out.println(arr[front + 1]);
}
}
注意
代码暂时写完了,但是这个队列是有问题的,它是一个一次性队列,没有达到复用的效果!!!
修改方案
将这个数组使用算法,改进成一个环形的队列。
数组模拟环形队列
对前面的数组模拟队列的优化,充分利用数组,因此将数组看做是一个环形的。(通过取模的方式来实现即可)
分析
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在判断队列满的时候需要注意:当(rear+1)%maxSize == front,代表队列满了。
- 当rear == front,代表队列为空。
- 分析示意图
思路
- front变量的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素front的初始值=0;
- rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定,rear的初始值=0;
- 当队列满时,条件是(rear+1)%maxSize;
- 对队列为空的条件,rear == front;
- 当我们这样分析,队列中有效的数据的个数(rear+maxSize-front)%maxSize;
- 我们就可以在原来的队列上修改得到,一个环形队列。
代码如下:
package cn.datastructures.queue;
import java.util.Scanner;
/**
测试数组模拟环形队列
* @author 最皮双皮奶
*/
public class CircleQueueDemo {
public static void main(String[] args) {
//创建队列
CircleQueue queue = new CircleQueue(5);
//创建一个值接收用户输入的操作数据
char key = ' ';
Scanner scanner = new Scanner(System.in);
boolean flag = true;
//输出一个菜单
while (flag) {
System.out.println("s:展示队列");
System.out.println("a:添加数据到队列");
System.out.println("g:取出队列中的数据");
System.out.println("h:查看队列头数据");
System.out.println("e:退出程序");
//key接收用户输入的字符
key = scanner.next().charAt(0);
//判断用户输入的数据,进行操作
switch (key) {
//展示数据
case 's':
try {
queue.showQueue();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//添加数据
case 'a':
try {
System.out.println("请输入一个数");
int i = scanner.nextInt();
queue.addData(i);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//获取数据
case 'g':
try {
int data = queue.getData();
System.out.println("数据为:" + data);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//展示头数据
case 'h':
try {
queue.headData();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
//退出
case 'e':
flag = false;
break;
default:
break;
}
}
System.out.println("退出程序!!!");
}
}
/**
用数组模拟环形队列
*/
class CircleQueue {
/**
表示队列前端的下标
*/
private int rear;
/**
表示队列后端的下标
*/
private int front;
/**
最大容量
*/
private int maxSize;
/**
用来模拟队列的数组
*/
private int[] arr;
/**
创建构造方法
*/
public CircleQueue(int maxSize) {
this.maxSize = maxSize;
//设置队列长度是maxSize,但是实际有效数据只有maxSize-1个。
arr = new int[maxSize];
// front=0; //front指向队列的第一个元素(arr[front]就是队列的第一个元素。)
// rear=0; //rear指向队列的最后一个元素的后一个位置,(因为需要空出一个空间,作为约定。)
}
/**
判断队列是否为空
*/
private boolean isEmpty() {
return rear == front;
}
/**
判断队列是否已满
*/
private boolean isFull() {
return (rear + 1) % maxSize == front;
}
/**
添加数据
*/
public void addData(int data) {
//判断队列是否已满
if (isFull()) {
throw new RuntimeException("队列已满,无法添加数据");
}
//加入数据
arr[rear] = data;
//将rear后移,考虑取模,以免数组越界。
rear = (rear + 1) % maxSize;
}
/**
取出数据
*/
public int getData() {
//判断队列是否为空
if (isEmpty()) {
//空的话抛出异常。
throw new RuntimeException("队列为空,无法获取数据");
}
//将当前第一个元素临时保存到data中。
int data = arr[front];
//将front后移,考虑取模,以免数组越界
front = (front + 1) % maxSize;
//将临时保存的变量data返回
return data;
}
/**
展示数据
*/
public void showQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列为空,无法显示数据");
}
//取出数据front会向上移,所以从front开始遍历,遍历(队列的有效数据的个数)个元素
for (int i = front; i < front + size(); i++) {
//i一直向上加会引起数组角标越界异常,所以要考虑取模!!!
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
}
}
/**
算出实际有效数据的个数
*/
private int size() {
return (rear + maxSize - front) % maxSize;
}
/**
展示头数据(front位置的数据)
*/
public void headData() {
if (isEmpty()) {
throw new RuntimeException("队列为空,无法显示头数据");
}
System.out.println(arr[front]);
}
}
感谢阅读!!!