java数据结构之----数组队列及循环队列
数据结构与算法学习的第二篇
什么时候需要用到队列
队列在实际开发过程中运用的非常广泛,由于我也是学习基础,还没有开发过什么项目,所以用到时候就知道了
什么是队列
- 队列是一个有序列表,可以用数组或者链表来实现
- 队列遵守先入先出的原则
联系实际场景
可以把一个队列的存入,读取数据的过程看成在银行里面排队办理业务,进银行,取号,叫号,办理业务,出银行这几个过程。
具体图示
实现思路
由上面的图示,可以知道数组队列需要以下几个属性来完成模拟队列的实现:
基本属性
- maxSize:表示队列的最大存储容量,也就是数组的大小
- front:队头属性
- rear:队尾属性
- arrQueue:模拟队列的数组
实现过程
- 队列的输入和输出是由队头和队尾属性控制的,因此由front和rear分别记录下队列的前后端下标
- 当front 的下标志值等于rear的下标值时,队列为空
- 当rear 的值等于 maxSize - 1时队列为满队列
- 加入数据时队尾的rear增加1
- 输出出队时队列的front减少1
实现上述方法需要创建的方法
- 判断队列是否是满队列的方法 isFull()
- 判断队列是否为空队列的方法 isEmpty()
- 增加数据的方法addQueue(),注意在增加数据时需要判断队列是否是满队列,是满队列的话数据增加不了
- 获取队列数据的方法getQueue(),注意获取队列数据时需要判断队列是否为空队列,不然会产生错误
- 显示队列中数据的方法 listQueue()
- 获取队列队头数据的方法 getHead()
代码实现
//测试队列
ArrayQueue queue = new ArrayQueue(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):获取队列的头数据");
System.out.println();
key = scanner.next().charAt(0); //表示接收用户输入的数据
switch (key) {
case 's': //展示队列中的数据
queue.listQueue();
break;
case 'e': //退出程序
scanner.close();
System.out.println("成功退出队列!");
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);
break;
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
case 'h': //显示队头数据
try {
int res = queue.headShow();
System.out.printf("队头数据是:%d",res);
System.out.println();
break;
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
default:
break;
}
}
System.out.println("程序已退出");
}
}
/**
* 1.使用数组模拟对列——编写一个 ArrayQueue 类
*/
class ArrayQueue{
private int maxSize; //用于表示数组的最大容量
private int front; //队列的头指针
private int rear; //队列的尾指针
private int []arrQueue; //用于存放数据的数组,模拟队列
//用于创建对列的构造器
public ArrayQueue(int n) { //给各个属性初始化
maxSize = n;
front = -1; //队头指向数组第一个位置的前一个
rear = -1; //队尾指向数组的前一个位置
arrQueue = new int[n]; //设置数组队列的大小
}
//判断队列是否满
//队列满的条件是 rear = maxSize -1; 因为数组的下标是从零开始的
public boolean isFull() {
return rear == maxSize -1;
}
//判断队列是否为空
//队列为空的条件是 front == rear
public boolean isEmpty() {
return front == rear;
}
//增加数据到对列中
public void addQueue(int n) {
//增加数据时需要判断队列是否为满
//为满的话,数据根本增加不进去
if(isFull()) {
System.out.println("队列是满的,无法加入数据");
return;
}
rear++; //添加数据时移动的队尾指针
arrQueue[rear] = n;
System.out.println("数据添加成功");
}
//获取队列中的数据,也叫出队
public int getQueue() {
//获取队列的数据时需要判断队列的是否为空
if(isEmpty()) {
throw new RuntimeException("队列为空,无法获取");
}
front++;//数据出队时移动的是队头指针
return arrQueue[front];
}
//显示队列中的数据
public void listQueue() {
//显示队列中的数据时需要先判断队列是否为空,为空则显示不了
if(isEmpty()) {
System.out.println("队列为空");
return;
}
for(int i=0; i<=maxSize-1; i++) {
System.out.printf("arr[%d] = %d", i,arrQueue[i]);
System.out.println();
}
}
//显示队列头数据
public int headShow() {
//显示队头数据也需要判断队列是否为空
if(isEmpty()) {
throw new RuntimeException("队列为空,无法显示");
}
return arrQueue[front+1];
这段代码实现后会发现,这个队列只能使用一次,当数组队列中的所有的位置都被使用过一次后,在将队列中的数据出队,新的数据无法添加到这个数组队列中,是因为,front和rear属性在执行方法时是单向移动,不能循环使用,所以当front移动到最后一个位置,rear也移动到最后一个位置后,虽然现实数组为空,但是添加不了数据,因为前面的空位rear去不了,后面又没有存储空间了,这就需要用到循环队列了。
差异代码展现
//判断是否为满
public boolean isFull() {
return (rear+1)%maxSize == front;
}
//添加数据
public void addQueue(int n) {
//增加数据时需要判断队列是否为满
//为满的话,数据根本增加不进去
if(isFull()) {
System.out.println("队列是满的,无法加入数据");
return;
}
//添加数据时不用再移动指向队尾的 rear
arrQueue[rear] = n;
//由于是循环队列所以 rear 可能会移动到数组的前面,
//单纯的 ++ 操作无法实现要求,可能还会越界
rear = (rear + 1) % maxSize;
System.out.println("数据添加成功");
}
//数据出队
public int getQueue() {
//获取队列的数据时需要判断队列的是否为空
if(isEmpty()) {
throw new RuntimeException("队列为空,无法获取");
}
//数据出队时就是 front 所指向的数组,不用后移动
int value = arrQueue[front];
front = (front + 1) % maxSize;
return value;
}
//显示队列
public void listQueue() {
//显示队列中的数据时需要先判断队列是否为空,为空则显示不了
if(isEmpty()) {
System.out.println("队列为空");
return;
}
//显示队列时不能仅仅以 数组大小来作为终止条件
//需要知道 在队列数组中一共有多少个有效数据
//借助 获取有效数据函数
for(int i=front; i<front+size(); i++) {
System.out.printf("arr[%d] = %d", i%maxSize,arrQueue[i%maxSize]);
System.out.println();
}
}
//获取有效数据个数
public int size() {
return (rear+maxSize-front) % maxSize;
}
循环数组队列和普通数组队列的不同处体现在这几个地方
- 判断队列是否为满队列 (rear+1)%maxSize == front,通过这样可以计算 rear 移动到数组之前用过的位置的值
- 添加数据时,通过下面的操作可以将 rear 移动到数组之前使用过的空间,重新来使用形成一个环。
arrQueue[rear] = n;
rear = (rear + 1) % maxSize;
- 需要添加一个方法,就是获取有效数据个数,
(rear+maxSize-front)%maxSize;
视频学习链接:https://www.bilibili.com/video/av54029771/?p=11
坚持者赢!!!