一.回顾
1.1自己用数组实现的缺点
上个博客完成了用数组实现队列的先进先出的基本功能了,但存在一定的缺点,就是不能重复使用,即一次性队列,在这篇将解决这个问题
二.数组模拟环形队列
2.1优化核心点
要想实现环形即循环,就要取模来完成,而不是简单的rear==maxSiz-1判断队列满了
2.2分析说明
尾索引的下一个为头索引时表示队列满,即将 队列容量空出一个作为约定,这个在做判断队列满的 时候需要注意 (rear + 1) % maxSize == front 满] rear == front 时为空队列不变
2.3思路说明
1) 对front 变量的含义做一个调整: front 变为指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素 front 的初始值 = 0
2) rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定. rear 的初始值 = 0
3. 当队列满时,条件是 (rear + 1) % maxSize == front
4. 对队列为空的条件, rear == front 空
5. 当我们这样分析时, 队列中有效的数据的个数为 (rear + maxSize - front) % maxSize 举个例子 :当 rear = 1 front = 0 maxSize =3 时,表示有几个有效数据呢,很明显首尾相减吗就一个数据,但当数据变多时还能相减吗,比如rear = 4 front = 0 maxSize =3 ,很显然4是大于maxSize的,不可能的,这时调用公式时计算 7%3=1,有一个有效数据,这样就完成了环形队列
2.4代码实现:
仅需要在上篇的博客代码中做一些小的修改即可得到环形队列了
package zy.queue;
import java.util.Scanner;
/**
* @author 十三
* @version 1.0
*/
public class circleQueue {
public static void main(String[] args) {
//检验一下
circleQ queue = new circleQ(4);
Scanner scanner = new Scanner(System.in);
char value = ' ';
boolean loop = true;
while (loop){
System.out.println("s:显示队列");
System.out.println("e:退出程序");
System.out.println("a:添加数据到队列");
System.out.println("g:从队列取出数据");
System.out.println("h:查看队列头数据");
value = scanner.next().charAt(0);
switch (value) {
case 's':
queue.showQueue();
break;
case 'e':
loop = false;
case 'a':
System.out.println("输入一个数:");
int key = scanner.nextInt();
queue.addQueue(key);
break;
case 'g':
try {
int result = queue.getQueue();
System.out.printf("取出的数据是:%d", result);
System.out.println();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int result = queue.headQueue();
System.out.printf("队列的头数据是:%d", result);
System.out.println();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("程序结束~");
}
}
class circleQ{
private int maxSize;//用来表示数组的最大容量
private int rear;//队列尾 rear指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定. rear 的初始值 = 0
private int front;//队列头 front变为指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素 front 的初始值 = 0
private int[] arr;//用于存放数据,模拟队列
public circleQ(int arrMaxSize){
maxSize =arrMaxSize;
arr = new int[maxSize];
}
// 下面创建一些方法
//判断队列是否满
public boolean isFull(){
return (rear + 1) % maxSize == front;//如果相等就是表示队列满,返回T
}
//判断队列是否为空
public boolean isEmpty(){
return front==rear;//我们知道当队列为空时,rear是和front相等的,都为-1,所以他们相等时,队列为空,返回T
}
//将数据添加到队列
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("队列为空,不能取出数据");
}
int temp = arr[front];
front = (front +1 )%maxSize;
return temp;
}
//显示队列的所有数据
public void showQueue(){
if (isEmpty()){
System.out.println("队列为空,没有数据");
return;
}
for (int i = front; i < front+size(); i++) {
System.out.printf("arr[%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];
}
}
2.5结果展示
可以看出,当队列中数据被取出完之后,调用addQueue方法,加入成功,至此,环形队列已经成功实现,也即实现了用数组完成队列的模拟
三.小结
1)和上篇的队列相比,环形队列就是在其基础之上,稍作修改即可,但虽是稍作修改但难做的是思维的修改,即想要完成环形队列,要想到取模的思想,因为取模本身就是一个环形(死循环)
2)其次还有一些小的细节,比如在全部展现队列的数据时,要和常规的for循环一样吗?,因为这个数组虽规定了大小,如果只是简单的for循环,我们好像不知道队列中有多少个有效数据,而且如果用arr.length的话,数组的长度是一直加着呢,会出现越界,所以前面提到的(rear + maxSize - front) % maxSize 就在这里用到了.
3)队列完结,下一篇线性结构常见的链表