数组模拟队列
队列概念
队列是个有序的数组,可以使用数组和链表模拟
遵循先入先出原则
队列的组成:
由上图可以看到,一个队列的基本组成大致可以包括以下几点:
- front(头指针)
- rear(尾指针)
- maxSize(队列最大容量)
- 一个数组结构(最重要的一个,因为我们就是用数组来模拟队列)
队列拥有两个指针:front(头)和rear(尾),初始指向-1这个数
front:随数据的输出改变
rear: 随数据的输入改变
队列的功能:
队列我们可以想象成银行叫号排队,既然是排队,那么就有入队和出队这两个主要的功能,但是我们又知道,人太多的话,他还是排不下的,所以我们有一个范围的控制。根据上述内容,我们具体分析一下入队和出队的具体思路。
入队功能:
入队,即往队列中加入数据。首先需要将尾指针后移一步,再在尾指针的位置加入数据。但是在加入之前我们要判断这个队列否是满的,如果是,则给出信息,直接return。具体步骤如下:
- 判断队列否是满的?是,则给出信息,直接return。
- 尾指针后移
- 将数据加入到队列的尾指针位置
出队功能:
出队,即在队列中取出数据。首先需要将头指针后移一步,再返回头指针的位置数据(这里的取出并没有删除数组中的数据)。但是在取出之前我们要判断这个队列否是空的,如果是,就不能返回数据,我们可以抛出一个异常,在使用时捕获这个异常即可。具体步骤如下:
- 判断队列否是空的?是,则抛出一个异常,在使用时捕获这个异常。
- 头指针后移
- 将头指针数据取出
以上是主要的两个功能,下面是代码结构和代码实现。
代码结构和代码实现
1. 创建一个主类和队列类:
public class ArrayQueueDemo {
public static void main(String[] args) {
// 主类
}
}
class ArrayQueue{
// 队列类
}
2. 队列类包含的属性以及构造器实现:
class ArrayQueue{
// 1.创建一个队列类
private int maxSize; // 队列的最大容量
private int front; // 头指针
private int rear; // 尾指针
private int[] arr; // 队列数组
// 2.创建构造器,初始化数据
// 传入一个参数,确定队列的最大容量
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; // 默认队列头是0,默认指向队列头前一个位置
rear = -1; // 默认队列头是0,默认指向队列尾数据
}
}
队列类拥有的主要功能:
class ArrayQueue{
// 1.创建一个队列类
private int maxSize; // 队列的最大容量
private int front; // 头指针
private int rear; // 尾指针
private int[] arr; // 队列数组
// 2.创建构造器,初始化数据
// 传入一个参数,确定队列的最大容量
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; // 默认队列头是0,默认指向队列头前一个位置
rear = -1; // 默认队列头是0,默认指向队列尾数据
}
// 3.判断队列是否为满
public boolean isFull(){}
// 4.判断队列是否为空
public boolean isEmpty() {}
// 5.添加队列数据
public void addQueue(int n) {}
// 6.取出队列数据
public int getQueue() {}
// 7.打印当前队列数据
public void showQueue() {}
// 8.显示队列的头数据是多少,不是取数据,是查数据
public int headQueue() {}
}
3. 判断队列是否为满:
因为我们只有一个数组,并且数组大小已知,所以当尾指针指向数组最后一个位置索引的时候,就是队列满的时候。
数组第一个元素索引:0,最后一个元素索引:maxSize-1.
即队列满的条件:rear == maxSize-1
// 3.判断队列是否为满
public boolean isFull(){
return rear == maxSize-1;
}
4. 判断队列是否为空:
我们以最开始的指针指向为例,两个指针都指向-1,并且没有元素,所以当两个指针指向的索引相等时,队列为空。
即队列空的条件:front == rear
// 4.判断队列是否为空
public boolean isEmpty() {
return front == rear;
}
5. 添加队列数据
实现步骤:
- 判断队列否是满的?是,则给出信息,直接return。
- 尾指针后移
- 将数据加入到队列的尾指针位置
// 5.添加队列数据
public void addQueue(int n) {
// 添加数据我们要考虑到队列是否已满
if (isFull()) {
System.out.println("队列满,不能再加入了");
return;
}
rear++; // 尾指针后移
arr[rear] = n; // 将数据加入到队列的尾指针位置
}
6. 取出队列数据
- 判断队列否是空的?是,则抛出一个异常,在使用时捕获这个异常。
- 头指针后移
- 将头指针数据取出
// 6.获取队列数据
public int getQueue() {
if (isEmpty()) {
// 因为这个方法具有返回值,我们不能返回一个数,这可能会有歧义
// 这里使用异常来结束这个方法,并在调用时try-catch即可
throw new RuntimeException("队列是空的,不能获取数据~\n");
}
front++; // 头指针后移
return arr[front]; // 将头指针数据取出
}
7. 打印当前队列所有数据–辅助功能
打印所有数据我们只需要使用for循环从头指针遍历到尾指针即可,在遍历之前我们可以判断一下队列是否是空的,如果是空的,我们就没必要遍历了。直接给一个提示,并return即可。
// 7.打印当前队列所有数据
public void showQueue() {
// 判断队列是否为空
if (isEmpty()) {
System.out.println("队列是空的,没有数据~");
return;
}
// 从头到尾打印,即使是用过的数据也打印出来
for (int i = 0; i < arr.length; i++) {
// 格式化打印
System.out.printf("arr[%d]=%d\n", i, arr[i]);
// 普通打印
// System.out.println("arr["+i+"]="+arr[i]);
}
/*
// 另一种遍历方式:只遍历队列里的数据
for (int i = front+1; i <= rear; i++) {
System.out.println("arr["+i+"]="+arr[i]);
}
*/
}
8. 显示队列的头数据–辅助功能
查看现在谁排在第一个,这个功能和取数据不一样,取数据头指针要后移,查看不需要。
实现步骤:
- 判断是否为空?是,则抛出异常,不返回数据.
- 返回arr[front+1]
// 8.显示队列的头数据是多少,不是取数据,是查数据
public int headQueue() {
// 判断是否为空? 是,则抛出异常,不返回数据.
if (isEmpty()) {
throw new RuntimeException("队列是空的,不能获取数据~");
}
return arr[front + 1];
}
到这里所有功能代码就完成了,接下来我将所有代码放到一起展示。
代码整合–ArrayQueue(队列类)
// 数组模拟队列-编写ArrayQueue类
class ArrayQueue {
// 1.编写主要属性
private int maxSize; // 队列的最大容量
private int front; // 头指针
private int rear; // 尾指针
private int[] arr; // 队列数组
// 2.创建构造器,初始化数据
// 传入一个参数,确定队列的最大容量
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; // 默认队列头是0,默认指向队列头前一个位置
rear = -1; // 默认队列头是0,默认指向队列尾数据
}
// 3.判断队列是否满
public boolean isFull() {
return rear == maxSize - 1;
}
// 4.判断队列是否为空
public boolean isEmpty() {
return front == rear;
}
// 5.添加队列数据
public void addQueue(int n) {
// 添加数据我们要考虑到队列是否已满
if (isFull()) {
System.out.println("队列满,不能再加入了");
return;
}
rear++;
arr[rear] = n;
}
// 6.获取队列数据
public int getQueue() {
if (isEmpty()) {
throw new RuntimeException("队列是空的,不能获取数据~\n");
}
front++;
return arr[front];
}
// 7.打印当前队列所有数据
public void showQueue() {
// 判断队列是否为空
if (isEmpty()) {
System.out.println("队列是空的,没有数据~");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
// 8.显示队列的头数据是多少,不是取数据,是查数据
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列是空的,不能获取数据~");
}
return arr[front + 1];
}
}
编写主类做测试
具体代码如下,比较简单,就不一 一介绍了,看注释。
package gll.queue;
import java.util.Scanner;
/**
* 数组模拟队列-
*/
public class ArrayQueueDemo {
public static void main(String[] args) {
// 测试
// 9.创建一个对象
ArrayQueue arrayQueue = new ArrayQueue(3);
// key当做功能按钮
char key = ' ';
Scanner sc = new Scanner(System.in);
// 10.编写文字界面,flag做退出标志
boolean flag = true;
while (flag) {
// 模拟功能页面
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 = sc.next().charAt(0);
switch (key) {
case 's':
// 查询所有数据
arrayQueue.showQueue();
break;
case 'a':
// 添加数据
System.out.println("请输入一个数:");
int n = sc.nextInt();
arrayQueue.addQueue(n);
break;
case 'g':
// 取出数据,有异常,需要手动抓取
try {
int result = arrayQueue.getQueue();
System.out.printf("取出的数据是%d\n", result);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
// 获取头数据,也有异常
try {
int result = arrayQueue.headQueue();
System.out.printf("队列的头数据是%d\n", result);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
// 退出系统,这里需要将scanner关闭.并修改flag
sc.close();
flag = false;
System.out.println("退出系统");
break;
}
}
}
}
// 数组模拟队列-编写ArrayQueue类
class ArrayQueue {
// 1.编写主要属性
private int maxSize; // 队列的最大容量
private int front; // 头指针
private int rear; // 尾指针
private int[] arr; // 队列数组
// 2.创建构造器,初始化数据
// 传入一个参数,确定队列的最大容量
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; // 默认队列头是0,默认指向队列头前一个位置
rear = -1; // 默认队列头是0,默认指向队列尾数据
}
// 3.判断队列是否满
public boolean isFull() {
return rear == maxSize - 1;
}
// 4.判断队列是否为空
public boolean isEmpty() {
return front == rear;
}
// 5.添加队列数据
public void addQueue(int n) {
// 添加数据我们要考虑到队列是否已满
if (isFull()) {
System.out.println("队列满,不能再加入了");
return;
}
rear++;
arr[rear] = n;
}
// 6.获取队列数据
public int getQueue() {
if (isEmpty()) {
throw new RuntimeException("队列是空的,不能获取数据~\n");
}
front++;
return arr[front];
}
// 7.打印当前队列所有数据
public void showQueue() {
// 判断队列是否为空
if (isEmpty()) {
System.out.println("队列是空的,没有数据~");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
// 8.显示队列的头数据是多少,不是取数据,是查数据
public int headQueue() {
if (isEmpty()) {
System.out.print("队列是空的,不能获取数据~");
throw new RuntimeException("队列是空的,不能获取数据~");
}
return arr[front + 1];
}
}
测试
测试可以自己去调一调,玩一下
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
a
请输入一个数:
10
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
s
arr[0]=10
arr[1]=0
arr[2]=0
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
a
请输入一个数:
20
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
a
请输入一个数:
30
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
s
arr[0]=10
arr[1]=20
arr[2]=30
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
a
请输入一个数:
40
队列满,不能再加入了
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
g
取出的数据是10
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
g
取出的数据是20
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
g
取出的数据是30
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
g
队列是空的,不能获取数据~
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
s
队列是空的,没有数据~
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列中取数据
h(head):查看队列头数据
e
退出系统
问题与优化
问题:队列不能复用: 上述代码还存在一些问题, 假如我的队列容量是3,那么他只能使用三次,之前使用过的位置不能重复使用,造成了浪费.数组不能复用.
解决:我们需要使用环形队列来解决。下一篇将讲述数组模拟环形队列,我们在原有的数组模拟队列基础上修改,减少工作量。
下一篇:java模拟环形队列
个人学习笔记,如有不足及时提出。