目录
1、队列
1.1 概念
2.2 队列的使用
2.3 队列模拟实现
2、循环队列
2.1 循环队列的认识
2.2 设计循环队列
3. 双端队列 (Deque)
1、队列
1.1 概念
队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)。入队列:进行插入操作的一端称为 队尾( Tail/Rear )出队列:进行删除操作的一端称为 队头 ( Head/Front )
2.2 队列的使用
队列的一些方法如下:
方法 |
功能
|
boolean offer(E e)
| 入队列 |
E poll()
| 出队列 |
peek() | 获取队头元素 |
int size()
| 获取队列中有效元素个数 |
boolean isEmpty() | 检测队列是否为空 |
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
举例:
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());
}
}
2.3 队列模拟实现
首先,我们定义一个MyLinkQueue类,队列的底层是由双向链表实现的,所以在类中我们定义一个节点内部类,代码如下:
public class MyLinkQueue {
static class ListNode {
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
public int usedSize; //队列中元素个数
}
接下来是队列相关操作的模拟实现,以下方法均是 MyLinkQueue类的成员方法。
入队列:
直接在链表的结尾加入一个节点即可。
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;
}
出队列:
有以下三种情况:
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 int size() {
return usedSize;
}
检测队列是否为空:
public boolean empty() {
return head == null;
}
2、循环队列
2.1 循环队列的认识
循环队列其实是由数组实现的
队列头:front 队列尾:rear
rear 是当前可以存放元素的下标,若rear在下标 7 的位置,如何来到下标 0 的位置呢?
通过 rear = (rear+1)%len (len为数组的长度)式子即可实现。
对于循环队列,我们如何区分是空是满呢,有以下方法:
- 通过添加 size 属性记录
- 保留一个位置(浪费一个空间表示满,浪费的空间不放元素)
- 使用标记
用保留一个位置(浪费一个空间表示满,浪费的空间不放元素)的方法,则:
循环队列为空的条件:rear = front ;
循环队列为满的条件: (rear+1)%len = front;
2.2 设计循环队列
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
class MyCircularQueue {
public int[] elem;
public int front;
public int rear;
public MyCircularQueue(int k) {
elem = new int[k];
}
//进入队列
public boolean enQueue(int value) {
if (isFull()) { //首先要判断队列是否满
return false;
}
elem[rear] = value; //不满,直接在rear位置插入元素
rear = (rear+1)%elem.length; //rear指向下一个位置
return true;
}
//出队列
public boolean deQueue() {
if (isEmpty()) { //首先要判断队列是否为空
return false;
}
front = (front+1)%elem.length; //将队头front指向下一个位置
return true;
}
//得到队头元素
public int Front() {
if (isEmpty()) {
return -1;
}
return elem[front];
}
//得到队尾元素
public int Rear() {
if (isEmpty()) {
return -1;
}
//在这里我们要判断一下rear此时的位置
//若rear在下标为 0 的位置,则返回index = elem.length-1位置的元素
//若不在下标为 0 的位置,则返回index = rear-1位置的元素
int index = (rear == 0)?elem.length-1:rear-1;
return elem[index];
}
public boolean isEmpty() {
return front==rear;
}
public boolean isFull() {
return front == (rear+1)%elem.length;
}
}
3. 双端队列 (Deque)
双端队列( deque )是指允许两端都可以进行入队和出队操作的队列, deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque是一个接口,使用时必须创建LinkedList的对象。
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。
Deque<Integer> stack = new ArrayDeque<>();// 双端队列的线性实现Deque<Integer> queue = new LinkedList<>();// 双端队列的链式实现