前言:
劳累了一天的博主又敲起了手中的键盘😭,来完成日更的承诺,这么勤奋的博主,谁不爱呢! 哈哈,来说说正题哈,今天,我们介绍的是队列,队列是继数组以来几乎是最简单的数据结构之一,所以相信我们的小伙伴们在看本篇博客的时候也会减少心理上和脑袋的负担,我将会从一下几点开始阐述:
队列的概述 🔜 普通队列的创建思路 🔜 普通队列的实现代码与分析 🔜 问题 🔜 环形队列的实现代码与分析 🔜 总结
如果喜欢作者的话 戳这!!!
往期精彩:
💞队列的概述:
队列是一个有序列表,该有序列表可以通过数组和链表去实现(博主用的是数组,因为比较有挑战性和思想性)
队列遵循先入先出,后入后出的原则!
❤️🔥普通队列的创建思路:
- 先创建一个数组,用于模拟队列
- 模拟队列的数组应该有最大容量
- 创建对应索引,用索引(rear 与 front)的行为去模拟队列先入先出的特征
- 注意: rear+1表示添加元素 front+1表示取出元素
💓普通队列的代码实现及分析
- 创建对应的实例变量
private int rear;
private int front;
private int maxSize;
private Object[] arrays;
public QueueArrays(int maxSize) {
this.maxSize = maxSize;
this.arrays = new Object[this.maxSize];
front = -1;
rear = -1;
}
代码分析:
- rear该索引表示队列的尾部,指向队列的最后一个元素,应该被赋值为-1
- front该索引表示队列的首部,指向队列的第一个元素的前一个位置,应该被赋值为-1
- maxSize为队列的最大容量,而下面的数组是为了放元素
- 通过构造方法创建队列(相信小伙伴都能明白)
图解:
-
创建两个实例方法,分别能判断队列是否满或者是否空
public boolean isFull() {
return rear == maxSize - 1;
}
public boolean isEmpty() {
return rear == front;
}
代码分析:
- 应为数组最大小标为maxSize-1,所以rear等于该值说明已经满了
- 刚开始rear与front的值都为-1,且为空,说明他们两个再次相等时,队列已空
图解:
-
创建一个添加元素的方法
public void addQueue(Object obj) {
if(isFull()) {
System.out.println("队列已满,无法放入");
return;
}
arrays[++rear] = obj;
}
代码分析:
- 当添加元素时,我们需要先判断一下队列是否已满,避免数组下标越界异常(校验)
- 因为要添加元素,假设是一个排队的人群,尾部开始延伸,所以这里的rear也要向后走一下,应为初始化为-1,所以要先++
图解:
-
创建一个取出元素的方法
public Object withdrawQueue() throws QueueException{
if(isEmpty()) {
throw new QueueException("队列已空,无数据");
}
return arrays[++front];
}
代码分析:
- 仍要校验一下队列是否为空
- 因为front表示的特殊性,所以还是要先++在取值
图解:
-
创建一个查看队列头的方法和展示队列的方法(不重要,故不展示分析)
public Object viewHead() throws QueueException {
if(isEmpty()) {
throw new QueueException("队列已空,无Head数据");
}
return arrays[front+1];
}
public void showQueue() {
for(var element : arrays) {
System.out.println(element);
}
}
完整代码
package datastructure.chapter01.queue;
/*
缺点
对于数组实现的队列,空间利用率低(每一个下标只能用一次,最后会导致空间越用越少)
*/
// 普通队列
public class QueueArrays {
private int rear;
private int front;
private int maxSize;
private Object[] arrays;
public QueueArrays(int maxSize) {
this.maxSize = maxSize;
this.arrays = new Object[this.maxSize];
front = -1;
rear = -1;
}
public boolean isFull() {
return rear == maxSize - 1;
}
public boolean isEmpty() {
return rear == front;
}
public Object withdrawQueue() throws QueueException{
if(isEmpty()) {
throw new QueueException("队列已空,无数据");
}
return arrays[++front];
}
public Object viewHead() throws QueueException {
if(isEmpty()) {
throw new QueueException("队列已空,无Head数据");
}
return arrays[front+1];
}
public void showQueue() {
for(var element : arrays) {
System.out.println(element);
}
}
}
问题:
你是否发现了,咱们队列的空间越用越少,用了这块空间以后都不能用了,是不是感觉很浪费,没有达到复用的效果,所以,我们推出了队列的plus版本,环形队列
由于环形队列的实现思路与普通队列一致,不过多阐述.(所创建的实例变量也一样)
环形队列的rear和front都应该被初始化为0
💘环形队列的代码实现及分析
-
创建对应的实例变量
private int sizeMax;
private int front;
private int rear;
private Object[] arrays;
public CircleQueueArrays(int sizeMax) {
this.sizeMax = sizeMax + 1;
arrays = new Object[this.sizeMax];
}
代码分析:
- rear指向数组的尾部,应该被赋值0,表示最后一个元素的后一个位置
- front指向数组的首部,应该被赋值0,表示第一个元素
图解:
-
创建两个实例方法,分别能判断队列是否满或者是否空
public boolean isFull() {
return (rear+1)%sizeMax == front;
}
public boolean isEmpty() {
return rear == front;
}
代码分析:
- 判断是否为空的判断是一致的,不在阐述
- 判断是否为满时,我给出一下分析
图解:
-
创建一个添加元素的方法
public void addQueue(Object element) {
if(isFull()) {
System.out.println("环形队列已满,无法添加数据");
return;
}
arrays[rear] = element;
rear = (rear+1)%sizeMax;
}
代码分析:
- 在添加元素之前,还是要校验一下队列是否已满.
- 由raer的性质可知,rear指向最后一个元素的后一个位置,所以,当我们添加元素的时候,可以直接添加到rear指向的位置,然后再让rear自增,注意:由于是环形队列,避免rear下标越界异常,需要%maxSize
图解:
-
创建一个取出元素的方法
public Object withdrawQueue() throws QueueException{
if(isEmpty()) {
throw new QueueException("环形数组已空,无法取出数据");
}
Object value = arrays[front];
front = (front+1)%sizeMax;
return value;
}
代码分析:
- 在取出数据之前,要校验一下队列是否为空
- 由front的性质可知,front指向第一个元素,所以直接取出即可,取出后,需要自增,此时的自增仍需要%maxSize避免下标越界异常
- 创建一个计算队列中元素个数的方法
public int size() {
return (rear-front+sizeMax)%sizeMax;
}
代码分析:
图解:
-
创建一个展示环形队列的方法
public void showQueue() {
for (int i = front; i < front+size(); i++) {
System.out.println("arr[" + i%sizeMax + "] = " + arrays[i%sizeMax]);
}
}
代码分析:
- 展示队列我们应该从第一个元素开始,所以初始化表达式将front赋予i
- 一共有size个元素.所以要front+size
- 因为front+size可能会大于maxSize,避免下标越界异常,仍是要取模一个maxSize
完整代码:
package datastructure.chapter01.queue;
//环形队列
public class CircleQueueArrays {
private int sizeMax;
private int front;
private int rear;
private Object[] arrays;
public CircleQueueArrays(int sizeMax) {
this.sizeMax = sizeMax + 1;
arrays = new Object[this.sizeMax];
}
public boolean isFull() {
return (rear+1)%sizeMax == front;
}
public boolean isEmpty() {
return rear == front;
}
public void addQueue(Object element) {
if(isFull()) {
System.out.println("环形队列已满,无法添加数据");
return;
}
arrays[rear] = element;
rear = (rear+1)%sizeMax;
}
public Object withdrawQueue() throws QueueException{
if(isEmpty()) {
throw new QueueException("环形数组已空,无法取出数据");
}
Object value = arrays[front];
front = (front+1)%sizeMax;
return value;
}
public Object viewHead() throws QueueException {
if(isEmpty()) {
throw new QueueException("环形数组已空,无法取出头数据");
}
return arrays[front];
}
public int size() {
return (rear-front+sizeMax)%sizeMax;
}
public void showQueue() {
for (int i = front; i < front+size(); i++) {
System.out.println("arr[" + i%sizeMax + "] = " + arrays[i%sizeMax]);
}
}
}
结论:
终于介绍完了普通队列和环形队列,特此,我提出一下几点需要注意的地方:
普通队列和环形队列的两个索引初始化有什么不同
两个队列中的两个索引表达的含义有什么不同
如何自增,加元素和取元素和自增的关系如何
下一站:????(随缘吧)