1. 使用场景
银行排队的案例:
2. 基本介绍
- 队列是一个有序列表,可以用数组或是链表来实现。
- 遵循先入先出的原则。即先存入队列的数据,要先取出;后存入的要后取出。
3. 数组模拟队列
思路
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize
是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front
和 rear
分别记录队列前后端的下标,front
会随着数据输出而改变,而 rear
则是随着数据输入而改变,如图所示:
当我们将数据存入队列时称为"addQueue
",addQueue
的处理需要有两个步骤:思路分析
1) 将尾指针往后移:rear+1
, 当 front == rear
【空】
2) 若尾指针 rear
小于队列的最大下标 maxSize-1
,则将数据存入 rear
所指的数组元素中,否则无法存入数据。rear == maxSize - 1
[队列满]
不足
- 数组只能使用一次,当队列中的数据取出后,并不能复用之前用过的空间。
- 优化参考环形队列
实现
- 添加元素,改变队尾指针 rear 的值;
- 移出元素,改变队头指针 front 的值;
- 其他操作不改变队头队尾的值,只在值基础上运算(取出队头为:front + 1)
数组队列
package com.zsy.datastructure.queue;
/**
* 使用数组模拟队列
*
* @author zhangshuaiyin
*/
public class ArrayQueue {
/**
* 队列最大空间
*/
private int maxSize;
/**
* front: 指向队列头的前一个位置,用于标识队列头
* rear:指向队列尾部
*/
private int front;
private int rear;
private int[] array;
public ArrayQueue(int arrayMaxSize) {
maxSize = arrayMaxSize;
array = new int[maxSize];
front = -1;
rear = -1;
}
/**
* @return 判断队列是否为空
*/
public boolean isEmpty() {
return front == rear;
}
/**
* @return 判断队列是否已满
*/
public boolean isFull() {
return rear == maxSize - 1;
}
/**
* 向队列添加元素
*
* @param item 元素
*/
public void add(int item) {
if (isFull()) {
System.out.println("队列已满,不能添加元素~~");
return;
}
// rear 后移,新增一个存储空间
rear++;
array[rear] = item;
}
/**
* 出队
*
* @return 头部数据
*/
public int remove() {
if (isEmpty()) {
throw new RuntimeException("队列为空不能取出数据!!");
}
return array[++front];
}
/**
* 打印队列中的元素
*/
public void show() {
if (isEmpty()) {
System.out.println("The queue is empty.");
return;
}
for (int i = 0; i < array.length; i++) {
System.out.printf("arr[%d]=%d\n", i, array[i]);
}
}
/**
* 打印队列头部元素
*/
public void head() {
if (isEmpty()) {
System.out.println("The queue is empty.");
return;
}
System.out.println("head: " + array[front + 1]);
}
}
测试
package com.zsy.datastructure.queue;
import java.util.Scanner;
/**
* @author zhangshuaiyin
*/
public class ArrayQueueTest {
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(3);
// 接收输入字符
char key;
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("****************");
System.out.println("s(show): 显示队列");
System.out.println("a(add): 添加数据");
System.out.println("g(get): 取出数据");
System.out.println("h(head): 头部数据");
System.out.println("按其他任意键退出...");
System.out.println("****************");
key = scanner.next().charAt(0);
switch (key) {
case 's':
queue.show();
break;
case 'a':
System.out.println("请输入要添加队列的数字:");
int value = scanner.nextInt();
queue.add(value);
break;
case 'g':
try {
System.out.println("取出的数据:" + queue.remove());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
queue.head();
break;
default:
scanner.close();
loop = false;
System.out.println("程序退出~~");
break;
}
}
}
}
4. 数组循环队列
参考《大话数据结构》
思路分析
- front:指向队列的第一个元素,初始值为0;
- rear:指向队列最后一个元素的下一个位置,且该位置为保留位,不会被数据占用;
- 当队列满时,rear 应该指向 front 的前一个位置,即 (rear + 1) % maxSize == front();
- 当队列为空时,front == rear,即出队时 front 后移,当移动到保留位置,表示队列空;
- 队列中的元素个数:(rear - front + maxSize) % maxSize
- 当rear > front,长度为:rear - front
- 当 rear < front,长度为:(0 + rear) + (maxSize - front) = rear - front + maxSize
代码实现:
循环队列
package com.zsy.datastructure.queue;
/**
* 数组实现循环队列
*
* @author zhangshuaiyin
*/
public class CircleArrayQueue {
/**
* 数组最大长度,因为rear预留位,队列长度会少一位
*/
private int maxSize;
/**
* front: 指向队列头一个元素
*/
private int front;
/**
* rear: 指向队列最后一个元素的下一个位置
* rear指向的位置为保留位,不会被元素占用
*/
private int rear;
private int[] array;
public CircleArrayQueue(int arrayMaxSize) {
this.maxSize = arrayMaxSize;
array = new int[maxSize];
this.front = this.rear = 0;
}
/**
* @return 判断队列是否为空
*/
public boolean isEmpty() {
return front == rear;
}
/**
* @return 判断队列是否已满
*/
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
/**
* 向队列添加元素
*
* @param item 元素
*/
public void add(int item) {
if (isFull()) {
System.out.println("队列已满,不能添加元素~~");
return;
}
array[rear] = item;
// rear 后移,当移动到最后一个位置时,从 0 重新计数
rear = (rear + 1) % maxSize;
}
/**
* 出队
*
* @return 头部数据
*/
public int remove() {
if (isEmpty()) {
throw new RuntimeException("队列为空不能取出数据!!");
}
// 取出数据为:array[front], 取出后 front 后移,当移动到最后一个位置时,从 0 重新计数
int value = array[front];
front = (front + 1) % maxSize;
return value;
}
/**
* 打印队列中的元素,从front开始到最后一个元素
*/
public void show() {
if (isEmpty()) {
System.out.println("The queue is empty.");
return;
}
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d]=%d\n", i % maxSize, array[i % maxSize]);
}
}
/**
* 打印队列头部元素
*/
public void head() {
if (isEmpty()) {
System.out.println("The queue is empty.");
return;
}
System.out.println("head: " + array[front]);
}
/**
* @return 获取队列数据个数
*/
public int size() {
return (rear - front + maxSize) % maxSize;
}
}
测试
package com.zsy.datastructure.queue;
import java.util.Scanner;
/**
* @author zhangshuaiyin
*/
public class CircleArrayQueueTest {
public static void main(String[] args) {
CircleArrayQueue queue = new CircleArrayQueue(3);
// 接收输入字符
char key;
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("****************");
System.out.println("s(show): 显示队列");
System.out.println("a(add): 添加数据");
System.out.println("g(get): 取出数据");
System.out.println("h(head): 头部数据");
System.out.println("按其他任意键退出...");
System.out.println("****************");
key = scanner.next().charAt(0);
switch (key) {
case 's':
queue.show();
break;
case 'a':
System.out.println("请输入要添加队列的数字:");
int value = scanner.nextInt();
queue.add(value);
break;
case 'g':
try {
System.out.println("取出的数据:" + queue.remove());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
queue.head();
break;
default:
scanner.close();
loop = false;
System.out.println("程序退出~~");
break;
}
}
}
}