目录
学习有关队列的知识请查看:【数据结构和算法9】自定义实现队列结构_逐步绽放的海棠花的博客-CSDN博客
1、概述
双端队列、队列、栈对比
定义 | 特点 | |
---|---|---|
队列 | 一端删除(头)另一端添加(尾) | First In First Out |
栈 | 一端删除和添加(顶) | Last In First Out |
双端队列 | 两端都可以删除、添加 | |
优先级队列 | 优先级高者先出队 | |
延时队列 | 根据延时时间确定优先级 | |
并发非阻塞队列 | 队列空或满时不阻塞 | |
并发阻塞队列 | 队列空时删除阻塞、队列满时添加阻塞 |
注1:
Java 中 LinkedList 即为典型双端队列实现,不过它同时实现了 Queue 接口,也提供了栈的 push pop 等方法
注2:
不同语言,操作双端队列的方法命名有所不同,参见下表
操作 JAVA JAVASCRIPT C++ LEETCODE 641 尾部插入 offerLast push push_back insertLast 头部插入 offerFirst unshift push_front insertFront 尾部移除 pollLast pop pop_back deleteLast 头部移除 pollFirst shift pop_front deleteFront 尾部获取 peekLast at(-1) back getRear 头部获取 peekFirst at(0) front getFront 吐槽一下 leetCode 命名比较 low
常见的单词还有 enqueue 入队、dequeue 出队
2、自定义双端队列的接口
/**
* 双端队列的接口定义
* 双端队列表示可以从队列头部或者尾部添加数据和删除数据
*
* @author zjj_admin
*/
public interface Deque<E> {
/**
* 从队列头部添加一个元素
*
* @param value
* @return
*/
boolean offerFirst(E value);
/**
* 从队列尾部添加一个元素
*
* @param value
* @return
*/
boolean offerLast(E value);
/**
* 移除并返回队列头部元素
*
* @return
*/
E pollFirst();
/**
* 移除并返回队列尾部元素
*
* @return
*/
E pollLast();
/**
* 查看队列头部元素
*
* @return
*/
E peekFirst();
/**
* 查看队列尾部元素
*
* @return
*/
E peekLast();
/**
* 队列是否为空
*
* @return
*/
boolean isEmpty();
/**
* 队列是否满了
*
* @return
*/
boolean isFull();
/**
* 队列元素个数
*
* @return
*/
int size();
}
3、使用双向循环链表实现双端队列
为什么使用双向循环链表数据结构实现,因为双端队列可以满足从队列头部和队列尾部添加或者删除数据,使用其他数据结构会提高代码的复杂度,并且使用双向循环链表恰好就能够满足双端队列的需求。
在这里双端循环链表指定一个哨兵节点 sentinel,此节点即表示头,也表示尾。只做标识不存储数据。
具体实现代码如下
/**
* 使用双向循环链表实现双端队列
* 使用双向链表可以方便的对队头和队尾进行操作
*
* @author zjj_admin
*/
public class LinkedListDeque<E> implements Deque<E>, Iterable<E> {
/**
* 双向链表数据结构
*
* @param <E>
*/
private static class Node<E> {
/**
* 上一个节点
*/
Node<E> prev;
/**
* 当前节点数据
*/
E value;
/**
* 下一个节点
*/
Node<E> next;
public Node(Node<E> prev, E value, Node<E> next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
/**
* 循环链表哨兵节点,既表示头,也表示尾
*/
private Node<E> sentinel;
/**
* 元素个数
*/
private int size;
/**
* 队列的容量
*/
private int capacity;
public LinkedListDeque(int capacity) {
if (capacity < 0) {
throw new IndexOutOfBoundsException("capacity 不能小于 0");
}
this.capacity = capacity;
size = 0;
// 初始化时,哨兵的 next 为哨兵,prev 也为哨兵
sentinel = new Node<>(null, null, null);
sentinel.next = sentinel;
sentinel.prev = sentinel;
}
/**
* 向队列的头部添加一个数据
* a added b,a为哨兵
*
* @param value
* @return
*/
@Override
public boolean offerFirst(E value) {
if (isFull()) {
return false;
}
//创建一个新节点,新节点的 prev 为哨兵,next 为哨兵的 next。
Node<E> a = sentinel;
Node<E> b = sentinel.next;
Node<E> added = new Node<>(a, value, b);
a.next = added;
b.prev = added;
size++;
return true;
}
/**
* a added b,b 为哨兵
*
* @param value
* @return
*/
@Override
public boolean offerLast(E value) {
if (isFull()) {
return false;
}
//创建新节点,新节点的上一个节点为 哨兵的 prev,新节点的下一个节点为哨兵
Node<E> a = sentinel.prev;
Node<E> b = sentinel;
Node<E> added = new Node<>(a, value, b);
a.next = added;
b.prev = added;
size++;
return true;
}
/**
* a first b,a 为哨兵
*
* @return
*/
@Override
public E pollFirst() {
//当队列为空时,返回 null
if (isEmpty()) {
return null;
}
Node<E> a = sentinel;
Node<E> first = a.next;
Node<E> b = first.next;
a.next = b;
b.prev = a;
size--;
return first.value;
}
/**
* a last b,b为哨兵
*
* @return
*/
@Override
public E pollLast() {
if (isEmpty()) {
return null;
}
Node<E> b = sentinel;
Node<E> last = b.prev;
Node<E> a = last.prev;
a.next = b;
b.prev = a;
size--;
return last.value;
}
@Override
public E peekFirst() {
if (isEmpty()) {
return null;
}
return sentinel.next.value;
}
@Override
public E peekLast() {
if (isEmpty()) {
return null;
}
return sentinel.prev.value;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == capacity;
}
@Override
public int size() {
return size;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
Node<E> p = sentinel.next;
@Override
public boolean hasNext() {
return p != sentinel;
}
@Override
public E next() {
E value = p.value;
p = p.next;
return value;
}
};
}
@SuppressWarnings("all")
@Override
public String toString() {
Node<E> p = sentinel.next;
E[] a = (E[]) new Object[size];
int i = 0;
while (p != sentinel) {
a[i] = p.value;
i++;
p = p.next;
}
return Arrays.toString(a);
}
}