概述
队列是一个有序列表,可以用数组或者是链表来实现
队列遵循“先入先出”原则,先存入队列的数据先取出,后存入的数据后取出。
数组模拟队列
基于数组的队列对象:
package com.leolee.dataStructure.queue;
/**
* @ClassName ArrayQueue
* @Description: TODO
* @Author LeoLee
* @Date 2020/9/10
* @Version V1.0
**/
public class ArrayQueue {
private int maxSize;
private int front;//队列头
private int rear;//队列尾
private int[] array;//该数组用于模拟队列,存放数据
public ArrayQueue (int maxSize) {
this.maxSize = maxSize;
array = new int[maxSize];
front = -1; //指向头部的前一个位置
rear = -1;//指向队列尾部(就是指向队列的最后一个数据)
}
//判断是否满数据
public boolean isFull () {
return (rear + 1) == maxSize;
}
//判断是否为空
public boolean isEmpty () {
return rear == front;
}
//数据入队列
public void addQueue (int n) {
if (isFull()) {
System.out.println("队列已满,不能加入数据");
return;
}
rear++;
array[rear] = n;
}
//数据出队列
public int outPutQueue () {
if (isEmpty()) {
throw new RuntimeException("队列已空,无法取到任何数据");
}
front++; //先入先出,front后移
return array[front];
}
public void showQueue () {
if (isEmpty()) {
System.out.println("队列已空,无数据展示");
return;
}
for (int i = 0; i < array.length; i++) {
System.out.printf("array[%d]:%d\n", i, array[i]);
}
}
//获取头数据
public int headQueue () {
if (isEmpty()) {
throw new RuntimeException("队列已空,无法取到任何数据");
}
return array[front + 1];
}
}
测试器:
package com.leolee.dataStructure.queue;
import java.util.Scanner;
/**
* @ClassName ArrayQueueDemo
* @Description: 数组模拟队列
* @Author LeoLee
* @Date 2020/9/10
* @Version V1.0
**/
public class ArrayQueueDemo {
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(4);
char key = ' ';
Scanner scanner = 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("o(outPut):出队列");
System.out.println("h(head):显示队列头部数据");
key = scanner.next().charAt(0);//接收一个输入字符
switch (key) {
case 's' :
arrayQueue.showQueue();
break;
case 'e' :
scanner.close();
loop = false;
break;
case 'a' :
System.out.println("请输入一个正整数");
int i = scanner.nextInt();
arrayQueue.addQueue(i);
break;
case 'o' :
try {
int result = arrayQueue.outPutQueue();
System.out.printf("取出的数据是:%d\n", result);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
break;
case 'h' :
try {
int headValue = arrayQueue.headQueue();
System.out.printf("头部数据是:%d\n", headValue);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
这些代码简单的模拟了队列的“先入先出”特性,但是这是一个有问题的demo,这个模拟队列中的“出”,实质上是通过 front 的“移动”来模拟的,“入”实质上是 rear 的“自增”模拟的,并没有实现真正的“出入”。由于有 maxSize 的限制,使这个队列变成了“一次性”的。怎么解决这个问题呢,往下看!
用数组模拟环形队列
针对上面的demo,需要调整如下:
- front 变量的含义从指向头部第一个元素的前一个位置,调整为:指向队列的第一个元素的位置(也就是说array[front]就代表队列第一个元素),所以front 初始值为0。
- rear 变量的含义从指向尾部元素的位置,调整为:指向队列最后一个元素的后面一个位置。因为希望腾出一个空间做约定使用(环形队列的最后一个元素位置腾出,不做元素的存储)。rear 的初始值为0。
- 队列满的条件由 (rear + 1) == maxSize,调整为:(rear + 1)% maxSize == front
- 队列为空的条件:rear == front
- 队列中有效数据的个数:(rear + maxSize - front) % maxSize
- 原先的“一段”队列变为了“环形”队列,首尾相接。
模拟环形队列:
package com.leolee.dataStructure.queue;
/**
* @ClassName CircleArrayQueue
* @Description: 环形队列数组
* @Author LeoLee
* @Date 2020/9/10
* @Version V1.0
**/
public class CircleArrayQueue {
private int maxSize;
private int front;//队列头,指向队列的第一个元素的位置(也就是说array[front]就代表队列第一个元素),所以front 初始值为0
private int rear;//队列尾,指向队列最后一个元素的后面一个位置。因为希望腾出一个空间做约定使用。rear 的初始值为0
private int[] array;//该数组用于模拟队列,存放数据
public CircleArrayQueue (int maxSize) {
this.maxSize = maxSize;
array = new int[maxSize];
//int默认值为0,可以不写以下两行
front = 0;
rear = 0;
}
//判断是否满数据
public boolean isFull () {
return (rear + 1) % maxSize == front;
}
//判断是否为空
public boolean isEmpty () {
return rear == front;
}
//数据入队列
public void addQueue (int n) {
if (isFull()) {
System.out.println("队列已满,不能加入数据");
return;
}
//直接加入数据
array[rear] = n;
//将rear后移
rear = (rear + 1) % maxSize;
}
//数据出队列
public int outPutQueue () {
if (isEmpty()) {
throw new RuntimeException("队列已空,无法取到任何数据");
}
//先取出front指向的第一个变量
int resultValue = array[front];
//front向后取一位,要考虑front是最后一位的情况,要取模
front = (front + 1) % maxSize;
return resultValue;
}
//显示环形队列
public void showQueue () {
if (isEmpty()) {
System.out.println("队列已空,无数据展示");
return;
}
//从front开始遍历
for (int i = front; i < front + getCurrentSize(); i++) {
System.out.printf("array[%d]:%d\n", i % maxSize, array[i % maxSize]);
}
}
//当前有效元素的个数
public int getCurrentSize () {
return (rear + maxSize - front) % maxSize;
}
//获取头数据
public int headQueue () {
if (isEmpty()) {
throw new RuntimeException("队列已空,无法取到任何数据");
}
return array[front];
}
}
测试器于之前逻辑一致,修改为 CircleArrayQueue 即可:
package com.leolee.dataStructure.queue;
import java.util.Scanner;
/**
* @ClassName CircleArrayQueueDemo
* @Description: 数组模拟环形队列
* @Author LeoLee
* @Date 2020/9/10
* @Version V1.0
**/
public class CircleArrayQueueDemo {
public static void main(String[] args) {
CircleArrayQueue circleArrayQueue = new CircleArrayQueue(4);//由于环形队列最后一位元素位置腾出,所以最大有效数据为3
char key = ' ';
Scanner scanner = 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("o(outPut):出队列");
System.out.println("h(head):显示队列头部数据");
key = scanner.next().charAt(0);//接收一个输入字符
switch (key) {
case 's' :
circleArrayQueue.showQueue();
break;
case 'e' :
scanner.close();
loop = false;
break;
case 'a' :
System.out.println("请输入一个正整数");
int i = scanner.nextInt();
circleArrayQueue.addQueue(i);
break;
case 'o' :
try {
int result = circleArrayQueue.outPutQueue();
System.out.printf("取出的数据是:%d\n", result);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
break;
case 'h' :
try {
int headValue = circleArrayQueue.headQueue();
System.out.printf("头部数据是:%d\n", headValue);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
运行程序观察结果,注意体会数组“收尾想借”的感觉,