介绍
1)队列是一个有序列表,可以用数组或链表实现
2)遵循先进先出原则
顺序队列
数组实现顺序队列思路分析
-
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量。
-
初始头部(front)、尾部(rear)时,front = -1,rear = -1;front指向队列头的前一个位置,rear指向队列尾部(即队列的最后一个数据)
-
因为队列的输入、输出分别是从尾部(rear)、头部(front)来实现的,因此需要两个变量front、rear分别记录队列头部、尾部的下标,front会随数据的输出而改变,rear会随数据的输入而改变,如图所示:
-
当我们将数据存入队列时,需要有两个步骤:
- 将指针后移:rear+1
- 尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指定的位置,否则无法存入数据,rear == maxSize-1【队列满】
- 当rear == front 时,表示队列为空
- 取出队列数据时,先判断队列是否为空,然后再取出并将头部后移:front+1
代码实现
package online.zavier.datastructures.queueOfArray;
import java.util.Scanner;
/**
* 使用数组实现队列
*/
public class QueueOfArray {
public static void main(String[] args) {
//接受用户输入
char key= ' ';
Scanner sc = new Scanner(System.in);
//创建数组顺序队列对象
queueOfArraySingle t = new queueOfArraySingle(5);
//创建数组环形队列对象
// queueOfArrayCircle t = new queueOfArrayCircle(5);
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':
t.showQueue();
break;
case 'e':
sc.close();
loop = false;
break;
case 'a':
System.out.println("请输入要添加的数据");
t.addQueue(sc.nextInt());
break;
case 'g':
try {
int res = t.getQueue();
System.out.printf("取出的数据为%d\n:",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = t.headQueue();
System.out.printf("获取头部的数据为%d\n:",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("退出程序...");
}
}
/**
* 数组实现顺序队列(创建的队列是一次性,队列满了后不可复用)
*/
class queueOfArraySingle {
//队列的最大容量
private int maxSize;
//队列头
private int front;
//队列尾
private int rear;
//用于存放数据,实现顺序队列
private int[] queueArr;
//构造器,其中参数maxSize是调用构造器时传过来的最大容量
public queueOfArraySingle(int maxSize){
this.maxSize = maxSize;
queueArr = new int[maxSize];
//表示指针指向队列头部的前一个位置,初始值为-1
front = -1;
//表示指针指向队列的尾部(最后一个数据),初始值为-1
rear = -1;
}
//判断队列是否已满
public boolean isFull(){
return rear == maxSize-1;
}
//判断队列是否为空
public boolean isEmpty(){
return front == rear;
}
//添加数据到队列
public void addQueue(int val) {
//判断队列是否已满
if (isFull()){
System.out.println("队列已满");
return;
}
//队列下标先自增再添加数据
queueArr[++rear]=val;
}
//取出头部数据,出队列
public int getQueue(){
//判断队列是否为空
if (isEmpty()){
throw new RuntimeException("队列为空");
}
return queueArr[++front];
}
//显示队列所有数据
public void showQueue(){
for (int i = 0;i < queueArr.length;i++){
System.out.printf("queueArr[%d]=%d\n",i,queueArr[i]);
}
}
//获取队列头部数据
public int headQueue(){
//判断队列是否为空
if (isEmpty()){
throw new RuntimeException("队列为空");
}
return queueArr[front+1];
}
}
顺序队列问题分析并优化
- 顺序队列使用一次就不能再使用了,没有达到复用效果
- 将这个数组使用算法,优化成环形队列,取模:%
环形队列
数组实现环形队列思路分析
- 当rear == front 时,表示队列为空
- 初始头部(front)、尾部(rear)时,front = 0,rear = 0;front指向队列的头部,即队列的第一个元素,rear指向队列的尾部的元素的后一个位置
- 尾索引的下一个为头索引时表示队列满,即将容器空出一个空间作为约定。算法:(rear+1)% maxSize == front
- 添加数据到队列时,先判断队列是否已满,若为false,则可添加到队列,再修改rear 的值(尾部指针后移):rear = (rear + 1)% maxSize
- 取出数据时,先判断队列是否为空,若为false,则可从队列取出数据,再修改front的值(头部指针后移):front = (front +1 ) % maxSize
maxSize指的是最大容量(包括空出的一个空间),而环形队列实际存放的数据(有效容量)= maxSize - 1
代码实现
package online.zavier.datastructures.queueOfArray;
import java.util.Scanner;
/**
* 数组实现队列
*/
public class QueueOfArray {
public static void main(String[] args) {
//接受用户输入
char key= ' ';
Scanner sc = new Scanner(System.in);
//创建数组顺序队列对象
// queueOfArraySingle t = new queueOfArraySingle(5);
//创建数组环形队列对象
queueOfArrayCircle t = new queueOfArrayCircle(5);
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':
t.showQueue();
break;
case 'e':
sc.close();
loop = false;
break;
case 'a':
System.out.println("请输入要添加的数据");
t.addQueue(sc.nextInt());
break;
case 'g':
try {
int res = t.getQueue();
System.out.printf("取出的数据为%d\n:",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = t.headQueue();
System.out.printf("获取头部的数据为%d\n:",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("退出程序...");
}
}
/**
* 数组实现环形队列
* 有效容量 = 最大容量(maxSize)-1;因为指针指向队列的尾部的后一个位置,所有环形队列能保存有效容量个数的值
*/
class queueOfArrayCircle {
//队列的最大容量
private int maxSize;
//队列头
private int front;
//队列尾
private int rear;
//用于存放数据,实现环形队列
private int[] queueArr;
//构造器,其中参数maxSize是调用构造器时传过来的最大容量
public queueOfArrayCircle(int maxSize){
this.maxSize = maxSize;
queueArr = new int[maxSize];
//表示指针指向队列头部,初始值为0
front = 0;
//表示指针指向队列的尾部的后一个位置,初始值为0
rear = 0;
}
//判断队列是否满了
public boolean isFull(){
//尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定
return (rear+1) % maxSize == front;
}
//判断是否为空
public boolean isEmpty(){
return rear==front;
}
//获取头部数据
public int headQueue(){
//判断队列是否为空
if (isEmpty()){
throw new RuntimeException("队列为空");
}
return queueArr[front];
}
//取出头部数据
public int getQueue(){
//判断队列是否为空
if (isEmpty()){
throw new RuntimeException("队列为空");
}
//创建一个临时变量,用于保存queueArr[front]对应的值
int val = queueArr[front];
//将front后移,考虑取模
front = (front+1)%maxSize;
//将临时保存的变量的值返回
return val;
}
//添加数据到队列
public void addQueue(int val){
//判断队列是否满了
if (isFull()){
System.out.println("队列已满");
return;
}
//将数据加入
queueArr[rear] = val;
//将rear后移,考虑取模
rear = (rear+1)%maxSize;
}
//显示队列所有数据
public void showQueue(){
for (int i = front;i<front+(rear-front+maxSize)%maxSize;i++){
System.out.printf("queueArr[%d]=%d\n",i%maxSize,queueArr[i%maxSize]);
}
}
}