数据结构之队列
队列简介
队列是一种特殊的线性表,只允许在表的前端(front)进行删除操作,表后端(rear)进行插入操作。进行删除操作的位置叫做对头,进行插入操作的位置叫做队尾。
特性
FIFO-First In First Out,先进先出存储数据。
队列实现主要分为顺序队列和循环队列。
- 顺序队列
建立顺序队列结构必须为其静态或者动态分配一片的连续存储空间,并且设置两个指针进行管理,一个是对头指针front,一个是队尾指针rear。
初始化队列时,rear=front=0,每次新增元素,rear增1;每次删除元素,front增1。随着新增和删除操作的依次进行,队列元素的个数不断变化,队列所占用的存储空间也在为队列结构所分配的空间中移动。当rear增加到指向分配的连续空间之外时,队列无法在插入元素,但这时往往还有大量的可用空间未被占用,这些空间是已经出队的队列元素曾经占用过的存储单元。
顺序队列的溢出情况:
1)“下溢”现象:当队列为空时,做出队运算产生的溢出现象。“下溢”是正常现象,常用于程序控制转义的条件。
2)“真上溢”现象:当队列满时,做入队运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。
3)“假上溢”现象:由于入队和出队操作中,头尾指针只增加不减少,导致被删除元素所占用过的空间无法被重新利用。当队列中实际的元素个数小于存储空间规模时,也可能由于尾指针已经到达存储空间的上界点而不能做入队操作。该现象称之为“假上溢”现象。
图示
顺序队列java代码数组实现
点击查看代码
/**
* 数据实现顺序队列,包含isFull()、isEmpty()、insert()、remove()、peekFront()基本操作。
* @date 2021/12/24
*/
@Data
@Slf4j
public class SequentialQueue {
/** 队列最大值 */
private int maxSize;
/** 数组实现 */
private Object[] queueArray;
/** 队头 */
private int front;
/** 队尾 */
private int rear;
public SequentialQueue(int maxSize) {
this.maxSize = maxSize;
this.queueArray = new Object[maxSize];
this.front = 0;
this.rear = 0;
}
/** 队列是是否已满 */
public boolean isFull() {
return rear == maxSize;
}
/** 队列是否为空 */
public boolean isEmpty() {
return rear == 0;
}
/** 入队 */
public void insert(Object object) {
// 队尾是否是最大值
boolean isFull = this.isFull();
if (isFull) {
log.error("队列已满,入队失败。");
} else {
queueArray[rear] = object;
rear++;
}
}
/** 出队,删除数据,对头后移 */
public void remove() {
if (this.isEmpty()) {
log.error("队列已空,出队失败。");
} else {
queueArray[front] = null;
front++;
}
}
/** 出队,删除数据,对头后移 */
public Object peekFront() {
return this.queueArray[front];
}
public static void main(String[] args) {
SequentialQueue queue = new SequentialQueue(3);
queue.insert(10);
queue.insert(23);
queue.insert(34);
log.info("入队后对头值,num = {}", queue.peekFront());
queue.remove();
log.info("出队后对头值,num = {}", queue.peekFront());
}
}
- 循环队列
在实际使用队列时,为了能使队列空间得到重复使用,往往会对队列的使用方法稍加改进。无论插入或删除操作,一旦rear指针或front指针增加1时超过了所分配的存储空间,就让它们指向存储空间的起始位置。指针从maxSize-1增1变为0,可用取余运算rear%maxSize和front%maxSize来实现。
实际上就是把队列空间想象成环形空间,在空间中的存储单元循环使用,用这种方法管理的队列就是循环队列。除了一些简单应用之外,真正实用的队列就是循环队列。
图示
循环队列Java数据实现
点击查看代码
/**
* 数组实现循环队列,包含insert()、remove()、peekFront()、isFull()和isEmpty()方法。
* @author Yoko
*/
@Data
@Slf4j
public class LoopQueue {
/** 队头 */
private int front;
/** 队尾 */
private int rear;
/** 实现数组 */
private Object[] queueArray;
/** 数组长度 */
private int maxSize;
/** 实际数据量 */
private int nItem;
public LoopQueue(int maxSize) {
this.maxSize = maxSize;
this.queueArray = new Object[maxSize];
this.front = 0;
this.rear = 0;
this.nItem = 0;
}
/** 队列是否为空 */
public boolean isEmpty() {
return this.nItem == 0;
}
/** 队列是否已满 */
public boolean isFull() {
return this.nItem == this.maxSize;
}
/** 入队 */
public void insert(Object object) {
if (this.isFull()) {
log.info("队列已满,入队失败。");
} else {
this.queueArray[rear] = object;
this.rear = (this.rear + 1) % this.maxSize;
this.nItem++;
}
}
/** 出队 */
public void remove() {
if (this.isEmpty()) {
log.info("队列已空,出队失败。");
} else {
this.queueArray[front] = null;
this.front = (this.front + 1) % this.maxSize;
this.nItem--;
}
}
/** 查看队头的值 */
public Object peekFront() {
if (!this.isEmpty()) {
return this.queueArray[front];
}
return null;
}
public static void main(String[] args) {
LoopQueue queue = new LoopQueue(4);
queue.insert(155);
queue.insert(10);
queue.insert(2);
log.info("入队后数据:loopArray = {}", queue);
log.info("对头的值:front= {}", queue.peekFront());
queue.remove();
log.info("出队后,queueArray = {}", queue);
log.info("出队后,对头的值:front= {}", queue.peekFront());
queue.insert(223);
queue.insert(15);
queue.insert(190);
log.info("再次入队后数据:loopArray = {}", queue);
}
}