队列(queue)
一种只允许一端进行添加数据(入队),另一端进行删除数据(出队)的线性数据结构;
它遵循“先进先出”原则,即FIFO;
以下Java代码使用数组模拟实现队列
1 基本队列
首先,基本队列起码都具有四个属性:
- maxSize 队列容量
- front 队列头指针(初始值为 -1)
- rear 队列尾指针(初始值为 -1)
- arr 存放队列数据的数组
注意:front指向的是头数据的前一个位置,rear则直接指向尾数据
基本队列实现比较简单,因此直接上代码:
package com.cxf.datastructures.queue;
import java.util.Scanner;
/**
* 数组模拟"队列"
*/
public class ArrayQueue {
private int maxSize;//数组最大容量
private int front;//队列头,指向头数据的前一个位置
private int rear;//队列尾,指向尾数据
private int[] arr;//数据数组
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(3);
Scanner scanner = new Scanner(System.in);
boolean flag = true;
char key = ' ';
while (flag) {
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':
try {
arrayQueue.show();
} catch (Exception e) {
e.printStackTrace();
}
break;
case 'a':
System.out.println("请输入一个数");
int num = scanner.nextInt();
arrayQueue.add(num);
break;
case 'g':
try {
int value = arrayQueue.get();
System.out.println("出队的数据是" + value);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 'h':
try {
int value = arrayQueue.headQueue();
System.out.println("当前头数据为" + value);
} catch (Exception e) {
e.printStackTrace();
}
break;
case 'e':
System.out.println("退出程序");
flag = false;
default:
break;
}
}
}
public ArrayQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
this.front = -1;
this.rear = -1;
this.arr = new int[maxSize];
}
//判断队列是否满
public boolean isFull() {
return rear == maxSize - 1;
}
//判断队列是否空
public boolean isEmpty() {
return rear == front;
}
//添加数据(入队列)
public void add(int n) {
if (isFull()) {
System.out.println("队列已满,无法添加数据");
return;
}
rear++;
arr[rear] = n;
}
//取出数据(出队列)
public int get() throws Exception {
if (isEmpty()) {
throw new Exception("队列为空,无法取出数据");
}
front++;
return arr[front];
}
//显示队列所有数据
public void show() throws Exception {
if (isEmpty()) {
throw new Exception("队列为空");
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
//查看队列头数据
public int headQueue() throws Exception {
if (isEmpty()) {
throw new Exception("队列为空,无法查看头数据");
}
return arr[front + 1];
}
}
这种队列的基本实现具有的缺点:无法复用,规定的队列大小用完,便只能丢弃,因为front和rear指针只能往后移动
2 环形队列
首先,环形队列也都具有四个属性:
- maxSize 队列容量
- front 队列头指针(初始值为 0)
- rear 队列尾指针(初始值为 0)
- arr 存放队列数据的数组
但是注意:
- 虽然也是用数组实现,但是要将数组想象成一个环形,即是前后端相连,其中的操作都要用过取模(求余)进行;
- maxSize要在队列尾预留出一个位置,用于实现“环形”;
- front不再指向头数据的前一个位置,而是直接指向头数据,所以初始值为0;
- rear不再直接指向尾数据,而是指向尾数据的后一个位置,初始值为0
2.1 相关判断
队列满:
(rear + 1) % maxSize == front
队列为空:
rear == front
队列中有效数据个数:
(rear + maxSize - front) % maxSize
2.2 例子
关键还是理解其中的取模运算,个人觉得其作用在于:实现环形。尽管其实模拟的数组下标不可能自己回到原始位置实现环形数组,通过%maxSize取模,就算下标一直增加,终于还是回到容量只有3的规定数组的正常下标
建议图例结合代码理解
2.3 代码实现
package com.cxf.datastructures.queue;
import java.util.Scanner;
/**
* 数组模拟"环形队列"
*/
public class CircleQueue {
//数组最大容量
private int maxSize;
//队列头,指向头数据,初始值为0
private int front;
//队列尾,指向尾数据的后一个位置,初始值为0
private int rear;
//数据数组
private int[] arr;
public static void main(String[] args) {
//实际队列容量为3,在容量中空余1位作为约定,以便实现环形,实现复用
CircleQueue circleQueue = new CircleQueue(4);
Scanner scanner = new Scanner(System.in);
boolean flag = true;
char key = ' ';
while (flag) {
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':
try {
circleQueue.show();
} catch (Exception e) {
System.out.println("队列为空");
}
break;
case 'a':
System.out.println("请输入一个数");
int num = scanner.nextInt();
circleQueue.add(num);
break;
case 'g':
try {
int value = circleQueue.get();
System.out.println("出队的数据是" + value);
} catch (Exception e) {
System.out.println("队列为空,无法取出数据");
}
break;
case 'h':
try {
int value = circleQueue.headQueue();
System.out.println("当前头数据为" + value);
} catch (Exception e) {
System.out.println("队列为空,无法查看头数据");
}
break;
case 'e':
System.out.println("退出程序");
flag = false;
default:
break;
}
}
}
public CircleQueue(int arrMaxSize) {
this.maxSize = arrMaxSize;
this.front = 0;
this.rear = 0;
this.arr = new int[maxSize];
}
//判断队列是否满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
//判断队列是否空
public boolean isEmpty() {
return rear == front;
}
//添加数据(入队列)
public void add(int n) {
if (isFull()) {
System.out.println("队列已满,无法添加数据");
return;
}
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
//取出数据(出队列)
public int get() throws Exception {
if (isEmpty()) {
throw new Exception();
}
int val = arr[front];
front = (front + 1) % maxSize;
return val;
}
//显示队列所有数据
public void show() throws Exception {
if (isEmpty()) {
throw new Exception();
}
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() throws Exception {
if (isEmpty()) {
throw new Exception();
}
return arr[front];
}
}