概述
- 队列是一个有序列表, 可以用数组或是链表来实现。
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。
(以下案例的front表示队列首位置、rear表示队列尾位置、maxSize表示队列的大小、数组arr用来模拟队列)
数组模拟单向队列
队列本身是有序列表,使用数组的结构来存储队列的数据时,maxSize 是该队列的最大容量。
因为队列的输出、输入分别时由前后位置处理的,因此需要变量front及rear分别记录队列前后的位置。
front会随着数据输出而改变,而rear 则是随着数据输入而改变
思路分析:
当我们将数据存入队列定义为addQueue,addQueue 的处理需要有两个步骤:
1)将尾指针往后移: rear+1, 当front==rear [空]
2)若尾指针rear 小于队列的最大下标maxSize-1, 则将数据存入rear所指的数组元素中,否则无法存入数据.
rear == maxSize - 1[队列满]
(单向队列)代码实现
package com.duanping.queue;
import java.util.Scanner;
/**
* 单向队列
* @author Savior.D
*
*/
public class Demo_ArrayQueue {
public static void main(String[] args) {
// 创建一个單向队列
ArrayQueue c = new ArrayQueue(4);
Scanner sc = new Scanner(System.in);
// 创建死循环的初始条件
boolean bool = true;
// 显示一个指令菜单
while (bool) {
System.out.println("a(add)-往队列中添加数据");
System.out.println("g(get)-往队列中取出数据");
System.out.println("s(show)-显示队列");
System.out.println("h(head)-查看队列头数据");
System.out.println("e(exit)-退出");
// 接收用户输入的指令
char ch = sc.nextLine().charAt(0);
switch (ch) {
case 'a':
System.out.println("请输入您要添加的数");
int num = Integer.parseInt(sc.nextLine());
c.addQueue(num);
break;
case 'g':
try {
System.out.println("取出的数据为" + c.getQueue());
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 's':
c.showQueue();
break;
case 'h':
try {
c.headQueue();
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 'e':
System.out.println("程序退出");
bool = false;
break;
default:
break;
}
}
}
}
class ArrayQueue {
// 数组的大小
private int maxSize;
// 数组用来存储数据,模拟队列
private int[] arr;
// 队列首位置
private int front;
// 队列尾位置
private int rear;
//创建队列的构造器,根据传进来的参数,设置队列的大小
public ArrayQueue(int maxSize) {
super();
this.maxSize = maxSize;
arr = new int[maxSize];
front = -1;
rear = -1;
}
// 判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
// 判断队列是否满了
public boolean isFull() {
return rear == maxSize - 1;
}
//向队列中添加数据
public void addQueue(int n) {
if (isFull()) {
System.out.println("队列已经满了,无法添加");
return;
}
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.println("arr[" + i + "]=" + arr[i]);
}
}
//显示队列的头部数据
public void headQueue() {
if (isEmpty()) {
System.out.println("队列为空,显示头部数据异常");
return;
}
System.out.println(arr[front + 1]);
}
}
单向队列的问题分析与优化方案
- 局限性: 目前数组使用一次就不能用, 你会在代码的运行结果中发现,当你取出队列中的所有数据后,无法再向队列中添加数据,没有达到复用的效果
- 将这个数组使用算法,改进成一个环形的队列取模: %
数组模拟环形队列
对前面的数组模拟队列的优化,充分利用数组.因此将数组看做是-个环形的。(通过取模的方式来实现即可)
分析:
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1) % maxSize== front 满]
- rear == front [空]
代码实现思路分析如下:
- front变量的含义做一个调整: front 就指向队列的第-一个元素也就是说arr[front]i就是队列的第-一个元素,front的初始值= 0
- rear变量的含义做一个调整: rear 指向队列的最后一个元素的后一个位置.因为希望空出一个空间做为约定.,rear的初始值=0
- 当队列满时,条件是(rear +1) % maxSize = front [满]
- 对队列为空的条件,rear= front [空]
- 当我们这样分析,队列中有效的数据的个数(rear-front+maxsize)% maxsize
- 我们就可以在原来的队列上修改得到,一个环形队列
(环形队列)代码实现
package com.duanping.queue;
import java.util.Scanner;
/**
* 环形队列
* @author Savior.D
*
*/
public class Demo_CircleArrayQueue {
public static void main(String[] args) {
//创建一个环形队列
CircleArray c = new CircleArray(4);//设定环形队列中的大小为4,其中有效数据为3个
Scanner sc=new Scanner(System.in);
//创建死循环的初始条件
boolean bool=true;
//显示一个指令菜单
while(bool) {
System.out.println("a(add)-往队列中添加数据");
System.out.println("g(get)-往队列中取出数据");
System.out.println("s(show)-显示队列");
System.out.println("h(head)-查看队列头数据");
System.out.println("e(exit)-退出");
//接收用户输入的指令
char ch=sc.nextLine().charAt(0);
switch (ch) {
case 'a':
System.out.println("请输入您要添加的数");
int num=Integer.parseInt(sc.nextLine());
c.addQueue(num);
break;
case 'g':
try {
System.out.println("取出的数据为"+c.getQueue());
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 's':
c.showQueue();
break;
case 'h':
try {
c.headQueue();
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 'e':
System.out.println("程序退出");
bool=false;
break;
default:
break;
}
}
}
}
class CircleArray{
//数组的大小
private int maxSize;
//数组用来存储数据,模拟队列
private int []arr;
//队列首位置
private int front;//默认为0
//队列尾位置
private int rear;//默认为0
//创建队列的构造器,根据传进来的参数,设置队列的大小
public CircleArray(int maxSize) {
this.maxSize = maxSize;
arr=new int[maxSize];
}
//判断队列是否为空
public boolean isEmpty() {
return rear==front;
}
//判断队列是否已满
public boolean isFull() {
return (rear+1)%maxSize==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("队列为空,取出数据异常");
}
/*取数据的注意事项
* 取出数据之后,位置要往后移
* 不能直接返回取出数据,因为这样方法就直接结束了,
* 没有上移的机会,所以此时应先需要创建一个临时变量
* 来存储要取出的值,然后接着进行后移的操作
* */
int value=arr[front];
//注意是环形队列要注意取模
front=(front+1)%maxSize;
return value;
}
//显示队列中的所有数据情况
public void showQueue() {
if(isEmpty()) {
System.out.println("队列为空,没有数据~");
return;
}
/* 遍历显示应该从第一个元素所在的位置处开始遍历,
* 遍历的个数应为队列中有效数据的个数(size()方法)
* */
for (int i = front; i < front+size(); i++) {
System.out.println("arr["+(i%maxSize)+"]="+arr[i%maxSize]);
}
}
//求出队列中有效数据的个数
public int size() {
return (rear-front+maxSize)%maxSize;
}
//显示此时队列中的头部数据,注意不是取出数据
public void headQueue() {
if(isEmpty()) {
throw new RuntimeException("队列为空,显示头部数据异常");
}
System.out.println("此时队列中的头部数据为"+arr[front]);
}
}
希望这篇文章能对你有所帮助,觉得还可以的可以点个赞支持一下~