1.1. 队列概念
1)队列是一个有序列表,可以用数组或是链表来实现。
2)队列只是在数组或者链表的存储基础上遵循了固定的先进先出的原则,FIFO而已。即:先存入队列的数据,要先取出。后存入的要后取出
3)示意图: (使用数组模拟队列示意图)
通常,称进数据的一端为 "队尾",出数据的一端为 "队头",数据元素进队列的过程称为 "入队",出队列的过程称为 "出队"。
1.1.1. 以数组实现队列为例
➢队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize 是该队列的最大容量。
➢因为队列的输出、输入是分别从前后端来处理,其中front代表队列头部,rear代表队列尾部,front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变。 如果队列增加数据,则依次往后增加,rear向后递增,如果队列中要减少数据,则从头部依次减少,front向后移动。
注意,头部front向后移动,有时候不代表前面的数据消失了,而是指我们这个队列的模型头部的指针向后移动了,前面的数据有可能还是在对应的内存地址中,我们只是通过程序的模拟来实现了队列的情况。
➢当我们将数据存入队列时称为”addQueue”,addQueue的处理需要有两个步骤:
1)将尾指针往后移: rear+1, 当front== rear [空]
2)若尾指针rear 小于队列的最大下标maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。maxSize- 1[队列满]
栈和队列不要混淆,栈结构是一端封口,特点是"先进后出";而队列的两端全是开口,特点是"先进先出"。
代码示例
package com.ethan.queue;
import java.util.Scanner;
import org.junit.Test;
public class ArrayQueueDemo {
@Test
public void arrayQueueTest() {
// 初始化一个队列
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("a(add): 添加数据到队列");
System.out.println("g(get): 取出队列头数据");
System.out.println("h(head):查看头队列");
System.out.println("e(exit):退出程序");
key = scanner.next().charAt(0);
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("请输入一个数据:");
int data = scanner.nextInt();
queue.addQueue(data);
break;
case 'g':
try {
int valueQueue = queue.getQueue();
System.out.println("取出的队列头数据为:"+valueQueue);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int valueHead = queue.headQueue();
System.out.println("查看队列头数据为:"+valueHead);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出···");
}
}
class ArrayQueue{
private int maxSize;//代表队列数组的最大容量
private int front;//代表队列数组的最大容量
private int rear;//代表队列数组的最大容量
private int[] arrQueue;//代表队列数组的最大容量
//1.初始化一个队列,构造函数
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arrQueue = new int[maxSize];
front = -1;// 指向队列头部,front不指向队列的头部第一个数据,指向队列头数据的前一个位置
rear = -1;// 指向队列尾部,指向队列尾部的最后一个数据,与front进行对比区分下
}
//2.判断是否为空,如果头指针和尾指针相等,代表队列中无数据
public boolean isEmpty() {
return front == rear;
}
//3.判断队列是否满,尾指针等于最大容量-1
public boolean isFull() {
return rear == maxSize-1;
}
//4.添加队列数据
public void addQueue(int queueData) {
// 判断队列是否满
if(isFull()) {
System.out.println("队列已满,无法添加新数据!");
return ;
}
rear++;
arrQueue[rear] = queueData;
}
//5.取队列数据
public int getQueue() {
if(isEmpty()) {
throw new RuntimeException("队列为空,不能取数据!");
}
front++;
return arrQueue[front];
}
//6.查看队列中全部数据(包含已经输出队列的数据
public void showQueueAll() {
if(isEmpty()) {
System.out.println("队列为空!");
return ;
}
for (int i = 0; i < arrQueue.length; i++) {
System.out.print(arrQueue[i]+"\t");
}
System.out.println();
}
//7.查看队列中数据
public void showQueue() {
if(isEmpty()) {
System.out.println("队列为空!");
return ;
}
for (int i = front+1; i < arrQueue.length; i++) {
System.out.print(arrQueue[i]+"\t");
}
System.out.println();
}
//8.查看头队列数据
public int headQueue() {
if(isEmpty()) {
throw new RuntimeException("队列为空,无法查看数据!");
}
return arrQueue[front+1];
}
}
1.2. 存在的问题
我们上面给出的是最基础的一个顺序队列功能,顺序队列整体后移造成的影响是:
顺序队列之前的数组存储空间将无法再被使用,造成了空间浪费,该队列数组使用一次就无法再次使用了,也就是整个队列容量添加赋值过一次就无法继续添加了,即使你取出来了头部的数据也不行,无法达到复用的功能;
如果顺序表申请的空间不足够大,则直接造成程序中数组 a 溢出,产生溢出错误,可以通过异常判断来避免;
解决方法:
可以进行优化,对该数组使用算法,改进成一个环形队列,核心是取模%
1.3. 队列关键点
- 队列的头指针和尾指针,其实只是移动了队列的地址而已,本质上没有删除内存中队列的数据,只是移动了地址而指向了下一个数据而已,这点是队列很核心的一点;
下一篇改进这个顺序队列为环形队列。