队列介绍
队列是一个有序列表,可以用数组或是链表来实现。
遵循先入先出的原则。即:先存入队列的数据,要先取出;后存入的要后取出。
使用数组模拟队列示意图:
数组模拟队列
思路分析
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变,如上图所示。
当我们将数据存入队列时称为”addQueue”,addQueue的处理需要有两个步骤:
将尾指针往后移:rear+1,当 front==rear 时队列为空
若尾指针rear小于队列的最大下标maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear==maxSize-1[队列满]
代码实现
package com.athome.queue;
import java.util.Scanner;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/23 15:30
*/
public class QueueDemo {
public static void main(String[] args) {
Queue queue = new Queue(3);
boolean isFlag = true;
Scanner scanner = new Scanner(System.in);
while (isFlag) {
System.out.println("a:添加元素");
System.out.println("g:取出元素");
System.out.println("h:显示头部元素");
System.out.println("s:显示全部元素");
System.out.println("e:退出");
System.out.print("请输入选项:");
String str = scanner.next();
char choose = str.charAt(0);
switch (choose) {
case 'a':
System.out.print("请输入添加的元素:");
int i = scanner.nextInt();
try {
queue.add(i);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'g':
try {
System.out.println(queue.get());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
System.out.println(queue.showHead());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 's':
try {
queue.showAll();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
isFlag = false;
break;
}
}
}
}
class Queue {
private final int maxSize;//控制队列的大小
private int front;//指向头部
private int rear;//指向尾部
private final int[] arr;//数组模拟队列
public Queue(int maxSize) {
this.maxSize = maxSize;
//将指向头尾的两个指针初始化为-1
front = -1;
rear = -1;
arr = new int[maxSize];
}
/**
* 判断队列是否为空
*
* @return 为空返回true,否则返回false
*/
public boolean isEmpty() {
return front == rear;
}
/**
* 判断队列是否满
*
* @return 队列满返回true, 否则返回false
*/
public boolean isFull() {
return rear + 1 == maxSize;
}
/**
* 添加元素到队列末尾
*
* @param num 待添加的数
*/
public void add(int num) {
if (isFull()) {
throw new RuntimeException("队列已满,不能添加!!!");
}
arr[++rear] = num;
}
/**
* @return 返回队列头的数据
*/
public int get() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能显示头部数据!!!");
}
return arr[++front];
}
/**
* 展示队列头部数据,注意不是取出该数据
*
* @return 返回队列头部数据
*/
public int showHead() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能显示头部数据!!!");
}
return arr[front + 1];
}
/**
* 展示所有数据
*/
public void showAll() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能显示头部数据!!!");
}
for (int i = 0; i < arr.length; i++) {
System.out.println("arr[" + i + "]=" + arr[i]);
}
}
}
数组模拟环形队列
上述代码的问题:
目前数组使用一次就不能用,没有达到复用的效果。
优化:
将这个数组使用算法,改进成一个环形的队列取模:%
分析说明:
1.尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意(rear+1)%maxSize==front[满]
2.rear==front[空]
代码实现
package com.athome.queue;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/23 15:50
*/
public class CircleQueue {
private final int maxSize;
private int front;//指向头部
private int rear;//指向尾部
private final int[] arr;//数组模拟环形队列
public CircleQueue(int maxSize) {
this.maxSize = maxSize;
//将头尾两个指针初始化为0
front = rear = 0;
//初始化数组
arr = new int[maxSize];
}
/**
* 判断队列是否为空
*
* @return 为空返回true,否则返回false
*/
public boolean isEmpty() {
return front == rear;
}
/**
* 判断队列是否满
*
* @return 队列满返回true, 否则返回false
*/
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
/**
* 添加元素到队列末尾
*
* @param num 待添加的数
*/
public void add(int num) {
if (isFull()) {
throw new RuntimeException("队列已满,不能添加!!!");
}
arr[rear] = num;
rear = (rear + 1) % maxSize;
}
/**
* @return 返回队列头的数据
*/
public int get() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能显示头部数据!!!");
}
int temp = arr[front];
front = (front + 1) % maxSize;
return temp;
}
/**
* 展示队列头部数据,注意不是取出该数据
*
* @return 返回队列头部数据
*/
public int showHead() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能显示头部数据!!!");
}
return arr[front];
}
/**
* 展示所有数据
*/
public void showAll() {
if (isEmpty()) {
throw new RuntimeException("队列为空,不能显示头部数据!!!");
}
for (int i = front; i < front + size(); i++) {
System.out.println("arr[" + i % maxSize + "]" + arr[i % maxSize]);
}
}
/**
* @return 返回当前队列的实际大小
*/
private int size() {
//说明:在环形队列中,尾部指针不一定都在头部指针的后面,所以这里这里需要分两种情况考虑:
//(1).尾部指针在头部指针后面:
//那么此时 rear-front 就是真实的当前队列大小,但是由于加上了 maxSize会导致整体结果变大,
//所以对 maxSize 取模,刚好能将多出来的 maxSize 剔除掉,余数就是真实的大小。
//(2).尾部指针在头部指针前面:
//那么此时 rear-front 为一个负值,且这个负值的相反数恰好就是当前队列的大小,此时加上 maxSize
//结果为正数,但这个结果是 maxSize 减去当前队列大小的那个值。
//即:假如 S1 + S2 = maxSize,其中S1为当前队列大小,S2就是(rear+maxSize-front)的结果。
//此结果对maxSize取模,即S2 % maxSize 结果为S1,即队列大小。
//总结 :(rear+maxSize-front)%maxSize <=> (rear-front)的绝对值.
//return Math.abs(rear - front);//这么返回程序也OK。
return (rear + maxSize - front) % maxSize;
}
}
主方法中测试:
package com.athome.queue;
import java.util.Scanner;
/**
* Description:
* Author:江洋大盗
* Date:2021/1/23 16:16
*/
public class CircleQueueDemo {
public static void main(String[] args) {
CircleQueue queue = new CircleQueue(4);
boolean isFlag = true;
Scanner scanner = new Scanner(System.in);
while (isFlag) {
System.out.println("a:添加元素");
System.out.println("g:取出元素");
System.out.println("h:显示头部元素");
System.out.println("s:显示全部元素");
System.out.println("e:退出");
System.out.print("请输入选项:");
String str = scanner.next();
char choose = str.charAt(0);
switch (choose) {
case 'a':
System.out.print("请输入添加的元素:");
int i = scanner.nextInt();
try {
queue.add(i);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'g':
try {
System.out.println(queue.get());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
System.out.println(queue.showHead());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 's':
try {
queue.showAll();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
isFlag = false;
break;
}
}
}
}
结语
只要能收获甜蜜,荆棘丛中也有蜜蜂忙碌的身影,未来的你一定会感谢现在努力的自己。