数据结构 ---[队列 (Queue)]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 ---[队列 (Queue)]相关的知识,希望对你有一定的参考价值。
队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)
只能从一端(队尾)添加元素,从另一端(队首)取出元素
先进先出(FIFO—first in first out)
自定义队列的基本操作实现
返回值类型 | 方法 | 注释 |
---|---|---|
void | enqueue( ) | 元素入队 |
E | dequeue( ) | 元素出队 |
int | getSize( ) | 查看元素个数 |
E | getFront( ) | 查看队首元素 |
boolean | isEmpty( ) | 判断队列是否为空 |
自定义队列基本操作的接口
/**
* 自定义队列的基本操作接口.
* @param <E> 泛型,数据类型;
* @author 小智
* @create 2021-07-25 12:43
*/
public interface MyQueue<E> {
/**
* 获取队列中的实际元素个数;
*/
int getSize();
/**
* 判断队列是否为空;
*/
boolean isEmpty();
/**
* 入队(添加元素);
* @param element 需要添加的元素;
*/
void enqueue(E element);
/**
* 出队(删除元素);
*/
E dequeue();
/**
* 获取队首元素;
*/
E getFront();
}
自定义队列的基本操作实现类
/**
* 自定义队列的基本操作实现类;
* @author 小智
* @create 2021-07-25 12:47
*/
public class MyQueueImpl<E> implements MyQueue<E> {
/**
* 队列的容器;
*/
private MyselfArray<E> myselfArray;
/**
* 无参构造;
*/
public MyQueueImpl() {
this(20);
}
/**
* 有参构造;
*/
public MyQueueImpl(int capacity) {
myselfArray = new MyselfArray<>(capacity);
}
/**
* 获取队列中的实际元素个数;
* @return
*/
@Override
public int getSize() {
return myselfArray.getSize();
}
/**
* 判断队列是否为空;
* @return
*/
@Override
public boolean isEmpty() {
return myselfArray.isEmpty();
}
/**
* 入队(添加元素);
* @param element 需要添加的元素;
*/
@Override
public void enqueue(E element) {
try {
myselfArray.addTail(element);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 出队(删除元素);
* @return
*/
@Override
public E dequeue() {
try {
return myselfArray.removeHead();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取队首元素;
* @return
*/
@Override
public E getFront() {
try {
return myselfArray.getHead();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* 输出队列;
*/
@Override
public String toString() {
StringBuilder sbl=new StringBuilder();
sbl.append("{队首:");
if(this.isEmpty()){
return null;
}
//只要队列不为空,就让元素出队;
while(!this.isEmpty()){
sbl.append(this.dequeue()+";");
}
String substring = sbl.substring(0, sbl.lastIndexOf(";"));
return substring+" 队尾}";
}
}
dequeue()操作的时间复杂度为O(n)
由于在出队( 删除元素 )时,数组后面的元素要进行前移,
为解决前移问题,可使用front
记录队首位置,使用tail
记录等待入队的元素位置;
即循环队列实现.
循环队列
为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。
关于循环队列
为解决前移问题,可使用
front
记录队首位置,使用tail
记录(待插入)等待入队的元素位置;
-
状况(1) : 规定当 front == tail ; 队列为空即队首位置与待插入位置指向同一位置时,
-
状况(2) : 当队列还未满时,可将要插入的元素,存到队列空余的位置;即使该位置是已经删除元素后、剩余的空位置.
-
状况(3) : 当队列满时,为避免与状况(1)中规定的空队列 front = = tail 相矛盾;当队列剩余1个空间时便认为已满. 即 ( tail + 1 ) %队列长度 == front 时,队列已满.
使用循环队列的主要目的是降低出栈时的时间复杂度,以空间换取时间.
循环队列的实现
自定义循环队列;
/**
* @create 2021-07-26 14:57
* 循环队列实现;
*/
public class MyLoopQueue<E> implements MyQueue<E> {
/**
* data: 队列的容器;
*/
private E[] data;
/**
* front:队首位置; tail:表示待插入元素位置;
*/
private int front, tail;
/**
* size: 队列的元素个数;
*/
private int size;
/**
* 无参构造方法;
*/
public MyLoopQueue() {
this(20);
}
/**
* 有参构造方法;
*/
public MyLoopQueue(int capacity) {
//注意要多出一个空间;
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
/**
* 查看队列元素个数;
*/
@Override
public int getSize() {
return this.size;
}
/**
* 判断队列是否为空的方法;
*/
@Override
public boolean isEmpty() {
return front == tail;
}
/**
* 调整容量大小的方法;
*/
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
//将源数组中的元素存入调整大小后的数组中(元素位置不能变);
for (int i = 0; i < size; i++) {
newData[i] = data[(front + i) % data.length];
}
tail = size;
front = 0;
data = newData;
}
/**
* 入队;
* @param element 需要添加的元素;
*/
@Override
public void enqueue(E element) {
//若队列已满;队列扩容;
if ((tail + 1) % data.length == front) {
resize(2 * data.length);
}
data[tail] = element;
size++;
tail = (tail + 1) % data.length;
}
/**
* 出队
* @return
*/
@Override
public E dequeue() {
//注意队列为空时的状况;
if (front == tail) {
return null;
}
E result = data[front];
size--;
front = (front + 1) % data.length;
//容量不足1/4时缩容;
if (size == data.length / 4 && data.length / 2 > 0) {
resize(data.length / 2);
}
return result;
}
/**
* 查看队首元素;
* @return
*/
@Override
public E getFront() {
if (front != tail) {
return data[front];
}
return null;
}
/**
* 输出队列;
*/
@Override
public String toString() {
StringBuilder sbl = new StringBuilder();
sbl.append("队列中有 " + size + " 个元素|");
sbl.append("队首{");
/* for (int i = 0; i < size; i++) {
sbl.append(data[front + i % size]);
if (i != size - 1) {
sbl.append(",");
}
}*/
//使用临时变量存储队首位置front;
int temp = front;
while (front != tail) {
sbl.append(data[front] + ",");
front = (front + 1) % data.length;
}
//循环打印元素之后,再将front初始值归还;
front = temp;
sbl.append("}队尾");
return sbl.toString();
}
}
测试:
public class Test {
public static void main(String[] args) {
//循环队列测试;
MyLoopQueue<Integer> myLoopQueue = new MyLoopQueue<>();
Random random = new Random();
for (int i = 0; i < 10; i++){
myLoopQueue.enqueue(random.nextInt(50));
System.out.println(myLoopQueue.toString());
}
//出队:
while (!myLoopQueue.isEmpty()){
Integer dequeue = myLoopQueue.dequeue();
//System.out.println(dequeue);
System.out.println(myLoopQueue.toString());
}
}
}
结果:
队列中有 1 个元素|队首{36,}队尾
队列中有 2 个元素|队首{36,47,}队尾
队列中有 3 个元素|队首{36,47,48,}队尾
队列中有 4 个元素|队首{36,47,48,40,}队尾
队列中有 5 个元素|队首{36,47,48,40,2,}队尾
队列中有 6 个元素|队首{36,47,48,40,2,19,}队尾
队列中有 7 个元素|队首{36,47,48,40,2,19,15,}队尾
队列中有 8 个元素|队首{36,47,48,40,2,19,15,19,}队尾
队列中有 9 个元素|队首{36,47,48,40,2,19,15,19,44,}队尾
队列中有10 个元素|队首{36,47,48,40,2,19,15,19,44,38,}队尾
队列中有 9 个元素|队首{47,48,40,2,19,15,19,44,38,}队尾
队列中有 8 个元素|队首{48,40,2,19,15,19,44,38,}队尾
队列中有 7 个元素|队首{40,2,19,15,19,44,38,}队尾
队列中有 6 个元素|队首{2,19,15,19,44,38,}队尾
队列中有 5 个元素|队首{19,15,19,44,38,}队尾
队列中有 4 个元素|队首{15,19,44,38,}队尾
队列中有 3 个元素|队首{19,44,38,}队尾
队列中有 2 个元素|队首{44,38,}队尾
队列中有 1 个元素|队首{38,}队尾
队列中有 0 个元素|队首{}队尾
以上是关于数据结构 ---[队列 (Queue)]的主要内容,如果未能解决你的问题,请参考以下文章
python 用于数据探索的Python代码片段(例如,在数据科学项目中)