前言
数据结构对开发者而然如果想往高处走,那么数据结构是必要要掌握的一门技术,在这里我们来说一下数据结构中的队列
一、队列是什么?
简单一点形容,队列就是我们日常排队的队列,那么有什么特点呐,我们通过生活的角度来看,排队就是为了有序的维护安全以及令先到的人先进入
这里就引出了队列两个个很重要的性质,即有序和先入先出
1. 有序:队列是有序的,因此我们可以通过数组或者链表来进行实现队列
2. 先入先出:先进入的可以先离开,后进入的后离开
3. 下面我会通过数组来实现队列
二、数组实现队列
1.分析队列
- 通过先入先出我们可以分析出队列需要两个指针分别指向队列的第一项和最后一项
- 因为是通过数组来实现所以需要一个数组以及数组长度
/**
* 在这里我们已只添加数子为例子,如果不想已数字为例的话可以改成string数组
*/
class Queue {
private int maxSize; // 数组长度
private int first; // 指向队列头,指向队列头的前一个位置
private int last; // 指向队列尾部,指向队列尾的数据
private int[] array; // 数组
// 我们需要一个构造方法来对数组初始化
public Queue(int maxSize){
// 定义数组长度
this.maxSize = maxSize;
array = new int[this.maxSize];
/*
解释一下为什么让first和last都为-1
first为指向队列头的前一个位置因为数组初始下标为0所以为-1
而为什么令last也为-1,last指的是队列尾数据而因为此时没有数据所以为
-1
*/
first = -1;
last = -1;
}
}
2.分析方法
队列的组成我们分析完,那么下一步我们就需要针对方法来进行分析;
- 添加方法(add)
- 获取方法(get)
- 展示方法(show)
除了这三个基础的方法外我们还需要什么方法呐?由于我们是通过数组来模拟队列那么我们一定要有一个判断数组是否为空或以及数组是否已满这两种情况,为空时不能获取,已满时不能添加
- 是否空(isEmpty)
- 是否满(isFull)
接下来我们通过代码来实现这些方法:
// 判断是否已满
public boolean isFull(){
return last == maxSize - 1;
}
//判断是否为空
public boolean isEmpty(){
return first == last;
}
// 添加
public void add(int num){
// 添加前我们首先要确认数组是否已满
if(isFull()){
// 如果已满我们需要抛出一个异常或者打印提示已满不能添加数据
System.out.println("队列满,不能加入队列")
return;
}
last++;
array[last] = num;
}
// 获取
public int get(){
// 获取我们需要判断队列是否为空,如果为空抛出异常不能返回-1或者0
if(isEmpty()){
throw new RuntimeException("队列为空不能取数据");
}
return array[first + 1];
}
// 展示
public void show(){
// 如果为空则没有数据需要结束该方法不继续执行
if (isEmpty()) {
System.out.println("队列为空,没有数据");
return;
}
for (int i = 0; i < array.length; i++) {
System.out.printf("array[%d]=%d\n", i, array[i]);
}
}
3.缺点
我们已经通过数组来进行队列的实现,可实际上我们这个队列有一个很大的缺陷,那就是不能二次使用,当队列满了之后就算你获取元素之后也无法再次往队列中添加新的元素,因为数组已经达到最大长度无法继续添加,那么我们应该如何解决这个缺陷呐?我们可以让他变成环形队列
三、数组实现环形队列
1.分析环形队列
环形队列里所需要的元素和队列是一模一样的,但是first以及last的含义则发生了变化
class Queue {
private int maxSize; // 数组长度
private int first; // 指向队列头,指向队列的第一个元素的位置
private int last; // 指向队列尾部,指向队列尾后一个位置
private int[] array; // 数组
// 我们需要一个构造方法来对数组初始化,这里frist和last为0
public Queue(int maxSize){
// 定义数组长度
this.maxSize = maxSize;
array = new int[this.maxSize];
}
}
这里我解释一下last为什么变成了指向最后一个数据后一个位置的原因,是因为需要预留出一个空间,为什么需要预留出一个空间我后面会进行解释
2.分析方法
方法其实也是和队列中的方法是一样的只是方法实现的过程发生了变化,
其中不同的是多出来一个方法为获取队列有效值的个数方法,来针对展示数据代码会详细解释
/*
在环形队列中判断是否已满的条件就改变了,我们需要通过取模来
判断队列是否已满,为什么需要通过模运算来进行已满的判断是由于
是一个环形所以last其实可以比first要小,所以我们需要通过取模
来进行判断是否已满
*/
public boolean isFull(){
// last + 1 是把预留的那部分加上来
// 比如maxSize = 5 ; first = 3 ; last = 2;
// 通过取模我们可以得出(last + 1 ) % maxSize = 3
return (last + 1) % maxSize == first;
}
//判断是否为空条件不变
public boolean isEmpty(){
return first == last;
}
// 获取队列有效数据个数
public int size(){
// 为什么要这么获取有效个数 是防止last - first出现负值
return (last + maxSize - fisrt) % maxSize;
}
// 添加
public void add(int num){
// 添加前我们首先要确认数组是否已满
if(isFull()){
// 如果已满我们需要抛出一个异常或者打印提示已满不能添加数据
System.out.println("队列满,不能加入队列")
return;
}
array[last] = num;
// 这里必须通过模运算来获取last负责会导致下标越界
last = (last + 1) % maxSize;
}
// 获取
public int get(){
// 获取我们需要判断队列是否为空,如果为空抛出异常不能返回-1或者0
if(isEmpty()){
throw new RuntimeException("队列为空不能取数据");
}
// 这里需要分析出first是指向队列的第一个元素
// 1、先把first对应的值保留到一个临时变量
// 2、将first后移,考虑取模放置下标越界
// 3、将临时保存的变量返回
int val = array[first];
first= (first+1) % maxSize;
return val;
}
// 展示
public void show(){
// 如果为空则没有数据需要结束该方法不继续执行
if (isEmpty()) {
System.out.println("队列为空,没有数据");
return;
}
// 获取值也需要改变初始位置为first的位置
// 那么我们的最大值应该是什么
// 最大值:当前位置 + 队列有效数据个数
for (int i = first; i < first + size(); i++) {
// 这里为什么是i % maxSize是为了防止下标越界
System.out.printf("arr[%d]=%d\n", i % maxSize, array[i % maxSize]);
}
}
总结
以上就是今天要讲的内容,介绍了队列是什么,以及我们如何通过数组来实现队列,以及数组来实现环形队列