死磕数据结构与算法——队列。才疏学浅,如有错误,及时指正
1. 基本概念
队列(FIFIO):一种特殊的线性表,符合先进先出的原则, 即先进入队列的元素,要先取出,后存入队列的元素后取出。
队尾:允许插入元素的一端。(数组的前端)
队头:允许删除元素的一端。(数组的后端)
2. 数组模拟队列
队列的输入、输出与队列的两端有关系,所以我们设置两个变量来记录队列的前端和末端。front(前端)即取出元素的一端、rear(后端)即插入元素的一端。
1. 模拟示意图
2. 思路解析
- 首先需要对模拟的数组进行初始化。先要获得数组的大小即队列的长度,创建数组,设置两个变量front和rear分别表示队列的前端(出队列)和后端(入队列)。front指向队列的第一个元素的前一个,rear指向队列的最后一个元素。
- 判断队列是否为空:当出队列时,需要将front往后移动一格,再把元素取出。进队列时,需要把元素插入之后,再将rear向后移动。当front与rear相等,即front==rear时,表示front不用往后移动就到了队列的末尾,所以此时队列为空。
- 判断队列是否满:当rear变量指向数组的最后一个位置时,即队列已经不能再插入元素了,所以此时队列为满状态。
- 把数据添加都队列,首先判断队列是否满,参上。满了即插入不了了,如果未满,则把rear往后移动一格,然后把插入的数据赋值到数组的rear位置上。
- 出队列:首先判断队列是否为空,参上。满了即没有元素可以出来了;如果不为空,则把front往后移动一格,然后把数组中再front位置上的数据取出。
- 显示队列头数据:因为front指向队列头的前一个位置,所以直接取出数组[front+1]位置上的元素即是此时队列的头数据。
3. 代码实现
class ArrayQueue {
private int maxSize; //表示数组的最大容量
private int front; //队列头-前端
private int rear; //队列尾-后端
private int[] arr; //该数组用于存放数据,实现模拟队列
//创建数组队列的构造器
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize]; //为数组指定大小
//front和rear的初始值都等于-1
front = -1; //指向队列头部,分析出front是指向队列头的前一个位置
rear = -1; //指向队列尾,指向队列尾的数据(即队列的最后一个数据)
}
//判断队列是否满,当rear的值指向数组的最后一个位置时,说明数组的最后一个位置有数据了,即队列满。
public boolean isFull() {
return rear == maxSize - 1;
}
//判断队列是否为空,当front和rear的值相等时,队列为空
public boolean isEmpty(){
return front == rear;
}
//添加数据到队列
public void addQueue(int n) {
//判断队列是否满
if(isFull()){
System.out.println("队列满,不允许加入数据!!");
}
rear++; //让rear 后移
arr[rear] = n; //把要插入的数据赋值给arr[rear]
}
//获取队列的数据,即出队列。即让队列中最先进去的元素出队列
public int getQueue() {
//判断队列是否空
if(isEmpty()){
//通过抛出异常
throw new RuntimeException( "队列空,不能取数据" );
}
front++; //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]; //此处是取出队头数据,不是出队列。所以不需要移动控制出队列的变量front
}
}
4. 存在的问题
数组只能使用一次,利用率低。
优化:把数组单向队列改成一个环形的队列。
3. 数组模拟环形队列
1. 实现:以取模的方式把数组看成一个环形的。
2. 示意图:
3. 思路:
- front: 此时指向队列的第一个元素。front初始值为0.
- rear:此时指向队列的最后一个元素的后一个位置。把空出来的那个位置作为一个约定。rear
初始值为0. - 队列满:(rear+1)% maxSize == front。
- 队列空: rear == front
- 队列中有效数据的个数:(rear + maxSize - front)% maxSize 。
4. 代码实现
class CircleArray {
private int maxSize; //表示数组的最大容量
//front 变量含义做一个调整:front 就指向队列的第一个元素,也就是arr[front]
//front 的初始值=0
private int front; //队列头
//rear 变量的含义做调整: rear 指向队列的最后一个元素的后一个位置,因为希望空出一个位置
//rear 的初始值=0
private int rear; //队列尾
private int[] arr; //该数组用于存放数据,模拟队列
//创建队列的构造器
public CircleArray(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
}
//判断队列是否满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
//判断队列是否为空
public boolean isEmpty(){
return front == rear;
}
//添加数据到队列
public void addQueue(int n) {
//判断队列是否满
if(isFull()){
throw new RuntimeException( "队列满,不能加入数据" );
}
arr[rear] = n;
//将rear 后移,此处必须考虑取膜
rear = (rear + 1) % maxSize;
}
//获取队列的数据,出队列。
public int getQueue() {
//判断队列是否空
if(isEmpty()){
//通过抛出异常
throw new RuntimeException( "队列空,不能取数据" );
}
//front 是指向队列的第一个元素
//1. 先把front 对应的值保存到一个临时变量
int value = arr[front];
//2. 将front后移。
front = (front + 1) % maxSize;
//3. 将临时保存的变量返回
return value;
}
//显示队列的所有数据
public void showQueue(){
//遍历
if(isEmpty()){
System.out.println("队列空的,没有数据");
return;
}
// 从front开始遍历,遍历多少个元素
//
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];
}
}
总结:本文通过数组的方式来实现对队列的模拟,包含了对队列的增、删、等基本操作的说明以及代码实现。实现了数组模拟基本的单向队列和使用数组模拟环形队列。