数据结构与算法——队列
1.队列
队列是一个有序列表,可以使用数组或者是链表来实现。
特点:先进先出
1.1数组模拟队列
队列本身是有序列表,使用数组模拟队列声明如下如所示:
首先需要两个变量(也可以称作指针)来表示队列的头和尾,头(front)、尾(rear),这里要强调的是:front是随着数据的输出(离队)而改变的而rear是随着数据的输入(入队)而改变的。
下面以入队为例进行分析:
入队的方法我们称为addQueue,入队分为两步:
1.首先需要判断队列目前的状态,如果rear小于队列的最大容量maxSize - 1,则此时可以入队,rear + 1。
2.如果此时队列满,也就是当rear的值等于maxSize - 1时,此时是无法入队的。
代码实现:
//首先创建一个arrayqueue的类
class ArrayQueue{
private int maxSize;//表示数组的最大容量
private int front;//指向队列头部
private int rear; //指向队列尾部
private int[] arr; //模拟队列,该数组用于存放数据
//创建队列的构造器,初始化队列
public ArrayQueue(int arrmaxSize){
this.maxSize = arrmaxSize;
arr = new int[maxSize];
front = -1; //指向队列头部,此时是指向队列头部的前一个位置
rear = -1; //指向队列的尾部,此时就是指向队列尾部的元素
}
//判断是否为空
public boolean isEmpty(){
return front == rear;
}
//判断队列是否满
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("队列空,无法出队");//throw本身会导致return
}
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 (isEmpty()){
throw new RuntimeException("队列为空,没有数据");
}
return arr[front + 1];
}
}
测试:
public class ArrayQueueDemo {
public static void main(String[] args) {
//测试
ArrayQueue arrayQueue = new ArrayQueue(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("g(get):出队");
System.out.println("h(head):显示头数据");
key = scanner.next().charAt(0); //接收一个字符
switch (key){
case 's':
arrayQueue.showQueue();
break;
case 'a':
System.out.println("输入一个数据");
int value = scanner.nextInt();
arrayQueue.addQueue(value);
break;
case 'g':
try {
int res = arrayQueue.getQueue();
System.out.printf("出队的数据为%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = arrayQueue.HeadQueue();
System.out.println("头数据为:" + res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
就目前来说。这里模拟的队列只能使用一次,没有达到复用效果,后面需要改经。
1.2 使用数组模拟环形队列 (取模的方式)
① front变量含义做调整:front指向队列的第一个元素,也就是arr[front]指向的就是队列的第一个元素,front的初始值为0
② rear变量调整:rear指向队列的最后一个元素的后一个位置,原因:希望留出一个位置做约定,这里需要说明的是这个所谓的预留空间是动态变化的,rear的初始值为0
③ 当队列满时,条件变为:(rear + 1)% maxSize = front
④ 当队列空的条件不变:rear = front
⑤ 队列中有效的数据的个数:(rear + maxSize - front) % maxSize //rear = 1; front = 0
代码修改后如下:
package adny.imau.queue;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
*
* @Author: zhiguo
* @Date: 2022/03/19/20:10
* @Description:
*/
public class CircleArrayQueueDemo {
public static void main(String[] args) {
//测试
CircleArray Queue = new CircleArray(5); //说明这里设置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("g(get):出队");
System.out.println("h(head):显示头数据");
key = scanner.next().charAt(0); //接收一个字符
switch (key){
case 's':
Queue.showQueue();
System.out.println("front:" + Queue.getFront());
System.out.println("rear:" + Queue.getRear());
break;
case 'a':
System.out.println("输入一个数据");
int value = scanner.nextInt();
Queue.addQueue(value);
System.out.println("front:" + Queue.getFront());
System.out.println("rear:" + Queue.getRear());
break;
case 'g':
try {
int res = Queue.getQueue();
System.out.printf("出队的数据为%d\n",res);
System.out.println("front:" + Queue.getFront());
System.out.println("rear:" + Queue.getRear());
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = Queue.HeadQueue();
System.out.println("头数据为:" + res);
System.out.println("front:" + Queue.getFront());
System.out.println("rear:" + Queue.getRear());
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class CircleArray{
private int maxSize;//表示数组的最大容量
private int front;//指向队列头部
private int rear; //指向队列尾部
private int[] arr; //模拟队列,该数组用于存放数据
public CircleArray(int arrMaxSize){
maxSize = arrMaxSize;
arr = new int[maxSize];
//此时 front 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;
}
//rear本身就指向最后一个数据,直接加入数据
arr[rear] = n;
//rear后移,这里必须考虑取模,因为会出现越界的情况
rear = (rear + 1) % maxSize;
}
//出队
public int getQueue(){
//判断队列是否空
if (isEmpty()){
//通过抛异常处理
throw new RuntimeException("队列空,无法出队");//throw本身会导致return
}
//这里需要分析出front是指向队列的第一个元素。
//1 . 先把front对应的数据保存到临时变量中
int value = arr[front];
//2 . 将front 后移 (如果直接返回 front就没有后移的机会了) 同样会出现越界情况(考虑取模)
front = (front + 1) % maxSize;
//3 . 将临时保存的变量返回
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(){
return (rear + maxSize - front) % maxSize;
}
//获取头数据
public int HeadQueue(){
//判断空
if (isEmpty()){
throw new RuntimeException("队列为空,没有数据");
}
return arr[front];
}
public int getFront() {
return front;
}
public int getRear() {
return rear;
}