双端队列(Double-ended Queue)
双端队列(Deque),读为“deck”,是一种类似于队列的元素的有序集合。
它拥有两端,队首和队尾,支持在两端插入和移除元素。
在某种意义上,这种混合的线性结构同时具有栈和队列的性质。
因此做双端操作可用于先入先出队列(FIFO), 做单端操作可做为下压栈(LIFO)。
场景:
正如阻塞队列适用于生产者-消费者模式,双端队列同样适用于另一种相关模式,即工作密取(Work Stealing)。在生产者-消费者设计中,所有消费者有一个共享的工作队列,而在工作密取设计中,每个消费者都有各自的双端队列。如果一个消费者完成了自己的双端队列中的全部工作,那么它可以从其它消费者双端队列末尾秘密地获取工作。密取工作模式比传统的生产者-消费者模式具有更高的可伸缩性,这是因为工作者线程不会在单个共享的任务队列上发生竞争。在大多数时候,它们都只是访问自己的双端队列,从而极大地减小了竞争。当工作者线程需要访问另一个队列时,它会从队列的尾部而不是头部获取工作,因此进一步降低了队列上的竞争程度。
https://book.douban.com/annotation/31593287/
Java 实现的 Deque的体系结构图中可以看到,实现一个 Deque 可以使用数组(ArrayDeque),同时也可以使用链表(LinkedList),还可以同实现一个支持阻塞的线程安全版本队列 LinkedBlockingDeque。
要求 deque 增删操作为常数时间 O(1),因此采用双向链表实现。
- | Deque | Randomized Queue |
---|---|---|
Non-iterator operations | Constant worst-case time | Constant amortized time |
Iterator constructor | Constant worst-case time | linear in current # of items |
Other iterator operations | Constant worst-case time | Constant worst-case time |
Non-iterator memory use | Linear in current # of items | Linear in current # of items |
Memory per iterator | Constant | Linear in current # of items |
头尾增加哨兵指针,简化边界处理。
内存占用分析:
public class Deque<Item> implements Iterable<Item> {
private Node header; // 48 bytes
private Node trailer; // 48 bytes
private int n; // 4 bytes
// 16 bytes (object overhead)
private class Node {
// 8 bytes (inner class extra overhead)
private Item item; // 8 bytes (reference to String)
private Node next; // 8 bytes (reference to Node)
private Node prev; // 8 bytes (reference to Node)
} // --------------------
// 48 bytes per node
Deque.java
/******************************************************************************
* Compilation: javac-algs4 Deque.java
* checkstyle-algs4 Deque.java
* findbugs-algs4 Deque*.class
* Execution: java-algs4 Deque < tobe.txt
* Dependencies: None
*
* A generic deque, implemented using a double linked list and two dummy
* nodes.
*
* % more tobe.txt
* to be or not to - be - - that - - - is
*
* % java-algs4 Deque < tobe.txt
* to be not that or be (2 left on deque)
*
******************************************************************************/
import java.util.Iterator;
import java.util.NoSuchElementException;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
public class Deque<Item> implements Iterable<Item> {
private Node header; // 48 bytes
private Node trailer; // 48 bytes
private int n; // 4 bytes
// 16 bytes (object overhead)
private class Node {
// 8 bytes (inner class extra overhead)
private Item item; // 8 bytes (reference to String)
private Node next; // 8 bytes (reference to Node)
private Node prev; // 8 bytes (reference to Node)
} // --------------------
// 48 bytes per node
// construct an empty deque
public Deque() {
header = new Node()