什么是队列?
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
应用场景比如银行排队,前面开始执行,后面开始排。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。 [1]
这里解释一下线性表:线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
队列是一个有序列表,可以用数组和链表实现。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
这里展示是用数组模拟一个队列的实现
MaxSize是数组的最大长度 front是队列的前端 rear是队列的后端
添加数组rear向后走,取出数据front向后走。先进先出。
用数组实现一个列表代码如下
这个类相当于一个队列,其中有队列包含的属性和方法。
/**
* 使用数组模拟队列,编写一个叫做ArrayQueue的类
*/
class ArrayQueue {
/**
* //数组的最大容量
*/
private int maxSize;
/**
* 队列头部
*/
private int front;
/**
* 队列尾部
*/
private int rear;
/**
* 该数组用于存放数据,模拟队列
*/
private int[] arr;
/**
* 创建队列的构造器
*
* @param arrMaxSize
*/
public ArrayQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
arr = new int[maxSize];
//front本身指向队列头的前一个位置
front = -1;
//rear指向队列尾部,就是队列的最后一个数据
rear = -1;
}
/**
* 判断队列是否满的
*
* @return
*/
public boolean isFull() {
return rear == maxSize - 1;
}
/**
* 判断队列是否为空
*/
public boolean isEmpty() {
return rear == front;
}
/**
* 添加数据到队列
*/
public void addQueue(int n) {
//判断队列是否满了
if (isFull()) {
System.out.println("队列满了不能加入");
return;
}
rear++;//让rear后移
arr[rear] = n;
}
/**
* 数据出队列
*/
public int getQueue() {
//判断队列是否空
if (isEmpty()) {
//如果队列为空 通过抛异常处理 会结束方法
throw new RuntimeException("队列空不能取数据");
}
//让头部后移
front++;
//将数据返回
return arr[front];
}
/**
* 显示队列的所有数据
*/
public void showQueue(){
//遍历
//判断队列是否为空
if(isEmpty()){
System.out.println("队列空的不能遍历");
return;
}
//循环遍历
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
/**
* 显示队列的头部是多少
*/
public int headQueue(){
//判断队列是否为空
if (isFull()){
throw new RuntimeException("队列空的,没有数据");
}
return arr[front+1];
}
}
测试如下
public static void main(String[] args) {
//测试队列,创建一个队列
ArrayQueue queue = new ArrayQueue(3);
//接收控制台输入
Scanner scanner = new Scanner(System.in);
//定义key进行选择操作
char key =' ';
//让程序循环
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 =scanner.next().charAt(0);
switch (key){
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("请输入一个数字");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 'g':
//取出方法可能会抛出异常
try {
int queue1 = queue.getQueue();
System.out.println("取出的数据是"+queue1);
}catch (Exception e){
//输出异常信息 这个异常信息就是方法里写的
//throw new RuntimeException("队列空不能取数据");
System.out.println(e.getMessage());
}
break;
case 'h':
//查看方法可能会抛出异常
try {
int queue1 = queue.headQueue();
System.out.println("查看的数据是"+queue1);
}catch (Exception e){
//输出异常信息 这个异常信息就是方法里写的
//throw new RuntimeException("队列空的,没有数据");
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop=false;
break;
default:
break;
}
System.out.println("程序退出");
}
}
优化数组空间复用
把数组模拟成环形队列,使用一个算法改成环形数组
测试同上这里就不写了,
采用牺牲一个存储单元,取模
队列满的图解:
已用空间的有效个数
环形队列的定义如下
class CircleQueue {
/**
* //数组的最大容量
*/
private int maxSize;
/**
* 队列头部
*/
private int front;
/**
* 队列尾部
*/
private int rear;
/**
* 该数组用于存放数据,模拟队列
*/
private int[] arr;
public CircleQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
arr = new int[maxSize];
//front本身指向队列头
front = 0;
//rear指向队列尾部的后一个位置
rear = 0;
}
/**
* 判断队列是否满的
*
* @return
*/
public boolean isFull() {
//为了rear的值不超出maxSize
//假如rear在最后一位 rear+1就会大于maxSize
return (rear + 1) % maxSize == front;
}
/**
* 判断队列是否为空
*/
public boolean isEmpty() {
return rear == front;
}
/**
* 添加数据到队列
*/
public void addQueue(int n) {
//判断队列是否满了
if (isFull()) {
System.out.println("队列满了不能加入");
return;
}
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
/**
* 数据出队列
*/
public int getQueue() {
//判断队列是否空
if (isEmpty()) {
//如果队列为空 通过抛异常处理 会结束方法
throw new RuntimeException("队列空不能取数据");
}
//第三方变量取出要返回的值
//假如直接返回front就没有后移的机会
int value = arr[front];
//让头部后移
front = (front + 1) % maxSize;
//将数据返回
return value;
}
/**
* 显示队列的所有数据
*/
public void showQueue() {
//遍历
//判断队列是否为空
if (isEmpty()) {
System.out.println("队列空的不能遍历");
return;
}
//循环遍历
for (int i = front; i < front + size(); i++) {
//假如取数据取到了最尾部就取模回去 取头部的
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
}
}
/**
* 求出当前队列的有效数据
*/
public int size() {
//尾部减去头部,加maxSize是怕rear小于front 相当于赛跑给他加一圈
//万一结果超过有效长度 所以取模
return (rear + maxSize - front) % maxSize;
}
/**
* 显示队列的头部是多少
*/
public int headQueue() {
//判断队列是否为空
if (isFull()) {
throw new RuntimeException("队列空的,没有数据");
}
return arr[front];
}
}