⭐ 作者:小胡_不糊涂
🌱 作者主页:小胡_不糊涂的个人主页
📀 收录专栏:浅谈数据结构
💖 持续更文,关注博主少走弯路,谢谢大家支持 💖
队列
- 1.什么是队列
- 2. 队列的使用
- 3. 队列的模拟实现
- 4. 循环队列
- 5. 双端队列(Deque)
1.什么是队列
队列: 只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear)出队列:进行删除操作的一端称为队头(Head/Front)
2. 队列的使用
在Java中,Queue是个接口,底层是通过链表实现的:
方法 | 功能 |
---|---|
boolean offer(E e) | 入队列 |
E poll() | 出队列 |
peek() | 获取队头元素 |
int size() | 获取队列中有效元素个数 |
boolean isEmpty() | 检测队列是否为空 |
Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
上述方法的实现:
import java.util.LinkedList;
import java.util.Queue;
public class Main {
//方法的实现
public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5); // 从队尾入队列
System.out.println(q.size());
System.out.println(q.peek()); // 获取队头元素
q.poll();
System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
if(q.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(q.size());
}
}
}
3. 队列的模拟实现
我们知道队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过线性表可以了解到常见的空间类型有两种:顺序结构和链式结构。
模拟实现队列:
public class MyQueue {
//双向链表节点
static class ListNode {
public int val;
public ListNode next;
public ListNode prev;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;//队头
public ListNode last;//队尾
public int usedSize;//队列元素个数
// 入队列---向双向链表位置插入新节点
public boolean offer(int val) {
ListNode node = new ListNode(val);
if(head == null) {
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = last.next;
}
usedSize++;
return true;
}
// 出队列---将双向链表第一个节点删除掉
// 1. 队列为空
// 2. 队列中只有一个元素----链表中只有一个节点---直接删除
// 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
public int poll() {
if(head == null) {
return -1;
}
int retVal = head.val;
if(head.next == null) {
head = null;
last = null;
return retVal;
}
head = head.next;
head.prev = null;//第一个节点删除
usedSize--;
return retVal;
}
// 获取队头元素---获取链表中第一个节点的值域
public int peek() {
if(head == null) {
return -1;
}
return head.val;
}
public boolean empty() {
return head == null;
}
public int size() {
return usedSize;
4. 循环队列
循环队列顾名思义就是首位相连的队列,自然界中的生产者消费者分解者模型可以使用循环队列来描述。而环形队列通常使用数组实现。
数组下标循环的小技巧
- 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
- 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) %array.length
如何区分空与满?
- 通过添加 size 属性记录
- 保留一个位置
- 使用标记
循环队列的实现:
class MyCircularQueue {
public int[] elem;
public int front;//队头
public int rear;//队尾
public MyCircularQueue(int k) {
elem = new int[k+1];
}
//队尾入队
public boolean enQueue(int value) {
if(isFull()) {
return false;
}
elem[rear] = value;
rear = (rear+1) % elem.length;
return true;
}
//队头出队
public boolean deQueue() {
if(isEmpty()) {
return false;
}
front = (front+1) % elem.length;
return true;
}
//得到队头元素
public int Front() {
if(isEmpty()) {
return -1;
}
return elem[front];
}
//得到队尾元素
public int Rear() {
if(isEmpty()) {
return -1;
}
int index = (rear == 0) ? elem.length-1 : rear-1;
return elem[index];
}
public boolean isEmpty() {
return front == rear;
}
public boolean isFull() {
return (rear+1)%elem.length == front;
}
}
5. 双端队列(Deque)
**双端队列(deque)**是指允许两端都可以进行入队和出队操作的队列。
Deque是一个接口,使用时必须创建LinkedList的对象:
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口:
Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现