1.2 队列
- 队列特性: 先进先出
- 队列是个有序列表,可以使用数组或链表实现
1.2.1 数组模拟队列(无环形)
-
队列本身就是有序列表,如果使用数组的结构来实现队列,那么队列数组的声明如下图:
-
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标, front 会随着数据输出而改变,而 rear 则是随着数据输入而改变,如队列介绍中图所示
-
maxSize是队列的最大容量
-
入队addQueue()思路分析:
- 1.入队时,将rear的指针+1,并且注意当队列为空的时候font == rear
- 2.若指针rear小于队列的最大下标maxSize-1时,才能够存入数据,否则的话无法存入数据 。注意当队列为满的时候 rear == maxSize -1
-
注意:font并没有直接指向队列数据的第一位,而是指向队列头的前一位,所以需要读取队列头的时候需要使用font+1
-
注意:本队列的缺点,没有实现环形队列,出队的数据其实还是在队列数组内,只是指向队列头的指针变化了。
实现代码:
import java.util.List;
import java.util.Scanner;
class MyQueue{
private int maxSize;// 队列最大容量
private int font;// 队列头指针
private int rear;// 队列尾指针
private int[] arrayQueue;// 队列数组
public MyQueue(int maxSize){ // 初始化队列
this.maxSize = maxSize;
font = -1;
rear = -1;
arrayQueue = new int[maxSize];
}
public boolean isEmpty(){ // 判断队列是否为空
return font == rear;
}
public boolean isFull(){ // 判断队列是否满了
return rear == maxSize-1;
}
public void addQueue(int a){
if(!isFull()){
rear++;
arrayQueue[rear] = a;
}else
System.out.println("队列满了!");
}
public int popQueue() {
if (!isEmpty()) {
font++;
return arrayQueue[font];
} else {
System.out.println("队列为空!");
return -1;
}
}
public int getHead(){
if (!isEmpty()){
return arrayQueue[font+1];
}else {
throw new RuntimeException("队列为空");
}
}
public void showQueue(){
if (!isEmpty()){
int count = 0;
for(int i=font+1;i<rear+1;i++){
System.out.println("Queue["+count+"]: "+arrayQueue[i]);
count++;
}
}else
System.out.println("队列为空!");
}
}
public class ArrayQueueDemo01 {
public static void main(String[] args) {
System.out.print("请输入队列的最大容量:");
Scanner scanner = new Scanner(System.in);
int maxSize = scanner.nextInt();
MyQueue myQueue = new MyQueue(maxSize);
boolean isOk = true;
while (isOk){
System.out.println("s(show):显示全部队列");
System.out.println("a(add):入队");
System.out.println("p(pop):出队");
System.out.println("h(getHead):获取队列头");
System.out.println("e(exit):退出");
System.out.print("请输入命令: ");
char key = scanner.next().charAt(0);
switch (key){
case 's': {
myQueue.showQueue();
break;
}
case 'a': {
System.out.print("请输入添加数据: ");
int add = scanner.nextInt();
myQueue.addQueue(add);
System.out.println("入队: "+add);
break;
}
case 'p': {
System.out.println("出队: "+myQueue.popQueue());
break;
}
case 'h': {
System.out.println("队列头: "+myQueue.getHead());
break;
}
case 'e': {
System.out.println("退出");
isOk = false;
break;
}
default:
System.out.println("输入错误!");
}
}
}
}
1.2.2 环形数组模拟队列
优化:
-
font指针的初始值为0,font直接指向队列的第一个数据,判断队列为空的条件为rear == font
-
rear指向队列的最后一个数据的后面一个位置,所以rear指针的初始值为0(前一位是-1),即
arrayQueue[rear-1] = 最后一个数据 ,
-
队列满的条件是:(rear +1) % maxSize = font
-
为了区分队满和队空,所以需要浪费一个空间作为指针的停靠,因此才造成了队满条件为**(rear+1)% maxSize = font**,
- 为什么需要+1:因为这个时候实际上rear指针在font指针前一个位置(浪费的空间内)
- 为什么需要%maxSize:避免出现数组下标越界的问题
-
所以整个队列内有效数据的个数为:((rear-1)-font+1)% maxSize,即**(rear-font+maxSize)% maxSize**
-
为什么需要添加maxSize?
-
因为队列是个循环队列,那么rear和font的下标值是一直处于变化的,所以会出现rear小于font的情况,这个时候只需要加上一个maxSize就可以将rear-font的值从负转为正,可以看下图帮助理解:
-
-
入队时rear : rear = (rear+1) % maxSize
-
出队时font : font = (font+1) % maxSize
-
如图:
- maxSize = 8,rear = 8, font = 1
- (rear+1) % maxSize = 1 = font 队满
一篇讲的很好的博客 通俗易懂讲解循环队列原理_派大星⭐的博客-CSDN博客(https://blog.csdn.net/qq_44280408/article/details/104017937)
实现代码:
import java.util.Scanner;
class circleQueue{
private int maxSize;// 队列最大容量
private int font;// 队列头指针
private int rear;// 队列尾指针
private int[] arrayQueue;// 队列数组
public circleQueue(int maxSize){ // 初始化队列
this.maxSize = maxSize+1;
font = 0;
rear = 0;
arrayQueue = new int[this.maxSize];
}
public boolean isEmpty(){ // 判断队列是否为空
return font == rear;
}
public boolean isFull(){ // 判断队列是否满了
return (rear+1)%maxSize == font;
}
public void addQueue(int a){ // 入队
if(!isFull()){
arrayQueue[rear%maxSize] = a;
rear = (rear+1) % maxSize;
}else
System.out.println("队列满了!");
}
public int popQueue() { // 出队
if (!isEmpty()) {
int temp = arrayQueue[font];
font = (font+1) % maxSize;
return temp;
} else {
System.out.println("队列为空!");
return -1;
}
}
public int getHead(){
if (!isEmpty()){
return arrayQueue[font];
}else {
throw new RuntimeException("队列为空");
}
}
public void showQueue(){
if (!isEmpty()){
int count = 0;
for(int i=font;i<font+getNums();i++){
// 注意要防止下标越界
System.out.println("Queue["+count+"]: "+arrayQueue[i%maxSize]);
count++;
}
}else
System.out.println("队列为空!");
}
public int getNums(){ // 获取当前队列内有效数据的个数
return (rear - font + maxSize) % maxSize;
}
}
public class ArrayQueryDemo02 {
public static void main(String[] args) {
System.out.print("请输入队列的最大容量:");
Scanner scanner = new Scanner(System.in);
int maxSize = scanner.nextInt();
circleQueue circleQueue = new circleQueue(maxSize);
boolean isOk = true;
while (isOk){
System.out.println("s(show):显示全部队列");
System.out.println("a(add):入队");
System.out.println("p(pop):出队");
System.out.println("h(getHead):获取队列头");
System.out.println("e(exit):退出");
System.out.print("请输入命令: ");
char key = scanner.next().charAt(0);
switch (key){
case 's': {
circleQueue.showQueue();
break;
}
case 'a': {
System.out.print("请输入添加数据: ");
int add = scanner.nextInt();
circleQueue.addQueue(add);
System.out.println("入队: "+add);
break;
}
case 'p': {
System.out.println("出队: "+circleQueue.popQueue());
break;
}
case 'h': {
System.out.println("队列头: "+circleQueue.getHead());
break;
}
case 'e': {
System.out.println("退出");
isOk = false;
break;
}
default:
System.out.println("输入错误!");
}
}
}
}