数据结构_用数组实现环形队列
思路分析:
一、front就指向队列的第一个元素,也就是说,arr[front]就是队列的第一个元素
二、rear就是指向队列的最后一个元素的后一个位置,我们需要空出这个rear指向的空间(牺牲这个空间)
三、现在环形队列中,当队列满时,条件为:( rear + 1 ) % maxSize == front
原本非环队列满的条件是 rear = maxSize - 1
我的解释是:

所以实际上这个环形队列实际的存储数据的长度(从0开始到x)为 maxSize - 2 ,也就是 这个空间的大小-1 ,剩下的这个空间被牺牲掉了,用来做判断这个环形队列是不是满的!
当然,上面的图从1开始是不严谨的,应该从0开始,从1开始只是我画的时候方便=-=
四、当队列为空的时候,条件仍然是 rear = front
五、队列中有效的数据的个数为( rear + maxSize - front ) % maxSize

这个公式的解释可以这样理解,首先rear加上maxSize也就是从数组的0位置开始到rear位置加上了容量大小,此时减去一个front就相当于数组中将这个占有数据的值往下推,推到从0位置到rear-front,
此时对maxSize取余就是相当于一个大于maxSize的数减去了一个maxSize所得到的余数,就是数组中有效存储的个数。
把这个公式这样写更好理解:
( rear - front + maxSize ) % maxSize
1、先不考虑这个环存取数据绕过一圈以上了,那么肯定有rear>front,那么rear-front就是存的有效个数
2、如果这个环存取数据也就rear和front通过一系列位移不知道是rear大还是front大
3、此时加上maxSize,就一定可以保证rear-front是一个正数,此时对maxSize取余可以得到有效个数了
代码部分:
package queue;
import java.util.Scanner;
public class CircleArrayQueueDEMO {
public static void main(String[] args) {
System.out.println("测试——————————");
System.out.println("创建一个环形队列");
CircleArrayQueue queue = new CircleArrayQueue(3);
char key = ' '; //接受用户输入的字符
Scanner sc = 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 = sc.next().charAt(0); //接受输入的字符
switch (key) {
case 's':
queue.ShowData();
break;
case 'a':
System.out.println("输入一个数");
int value = sc.nextInt();
queue.addQueue(value);
break;
case 'g':
try {
int res = queue.getQueue();
System.out.println("数据是:" + res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = queue.headData();
System.out.println("队列头数据是:" + res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
sc.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class CircleArrayQueue {
private int maxSize; //数组最大容量
private int front; //队列的头 arr[front]就是队列的头元素
private int rear; //指向队列最后一个元素的后一个位置(空间) arr[rear-1]就是队列的尾元素
private int[] arr; //队列中存放的数据,模拟队列
//队列的构造器
public CircleArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize+1; //加1是为了留给被牺牲的空间
arr = new int[maxSize];
}
//判断队列是否满
public boolean isfull() {
return (rear + 1) % maxSize == front;
}
//判断队列是否为空
public boolean isnull() {
return rear == front;
}
//添加数据到队列
public void addQueue(int n) {
//先判断是否为满
if (isfull()) {
System.out.println("队列满了,不能添加数据");
return;
}
arr[rear] = n; //上面判断了队列还没满,
rear++;// 那么rear指向的位置就是空的,再后移
}
//获取队列数据,取数据
public int getQueue() {
//判断队列是否为空
if (isnull()) {
//通过抛出异常
throw new RuntimeException("队列空,不能取数据");
}
//front是指向队列的第一个元素
//一、先把front对应的值保留到一个临时变量(如果直接返回的return后面的代码不会执行)
//二、将front后移 注意:front可能移到 maxSize-1 去了
//三、将临时保存的变量返回
int value = arr[front]; //任何数取余 它的余数不会超过除数
front = (front + 1) % maxSize; //取模是为了防止front越界,让到了maxSize-1就下去指向0
return value;
}
//显示队列所有的数据
public void ShowData() {
if (isnull()) {
System.out.println("队列为空");
}
//从front开始遍历,遍历队列中有效的数据个数,从front -> rear,但rear可能小于front,因此要考虑取模
//队列中有效长度为queueLength
int queueLength = (rear + maxSize - front) % maxSize;
if (front < rear) { //front在rear下面
for (int i = 0; i < queueLength; i++) {
System.out.println("队列第" + (i + 1) + "个元素为:" + arr[front + i]);
}
} else { //front在rear上面,(front+i)可能会越界 "任何数取余,它的余数不会超过除数"
for (int i = 0; i < queueLength; i++) {
System.out.println("队列第" + (i + 1) + "个元素为:" + arr[(front + i) % maxSize]);
}
}
//不用上面的if的话,可以用下面的偷懒写法:
// for (int i = front; i < front+size(); i++) {
// System.out.println("arr["+ (i % maxSize) +"]="+ (arr[i%maxSize]) );
// }
}
public int size() { //计算循环队列中存储的个数
return (rear - front + maxSize) % maxSize;
}
//显示队列的头数据 不是取出
public int headData() {
if (isnull()) {
throw new RuntimeException("队列是空的");
}
return arr[front];
}
}
浙公网安备 33010602011771号