Fact-checked by Grok 2 weeks ago

Double-ended queue

A double-ended queue (commonly abbreviated as deque and pronounced "deck") is an in that generalizes both queues and stacks by allowing the insertion and deletion of elements at either end of the sequence. This structure maintains a linear order of elements, with access restricted to the front and back endpoints, enabling flexible first-in-first-out (FIFO) or last-in-first-out (LIFO) behaviors depending on the operations used. The term "deque" was coined by E. J. Schweppe, as reported by , to describe this double-ended capability, distinguishing it from single-ended structures like traditional queues. The core operations of a deque typically include adding an element to the front (addFront, push_front, or push), adding to the back (addBack, push_back, or inject), removing and returning the front element (removeFront, pop_front, or pop), and removing and returning the back element (removeBack, pop_back, or eject). Additional utility operations often encompass checking the front or back element without removal (front or peekFront, back or peekBack) and determining if the deque is empty (isEmpty). These operations are designed to execute in constant time, O(1), to ensure efficiency for dynamic sequences. Variants like input-restricted or output-restricted deques limit insertions or deletions to one end, reducing to or as special cases. Deques are commonly implemented using a doubly linked list, which provides direct access to both endpoints via head and tail pointers, facilitating all operations in constant time without fixed size limitations. Alternatively, a circular array (or ) implementation uses modular indexing with head and tail indices that wrap around the array bounds, offering space efficiency but requiring resizing for growth. Both approaches avoid shifting elements for endpoint modifications, unlike single-ended arrays. In practice, deques find applications in algorithm design and , such as buffering streams where data arrives and is consumed from variable ends, simulating printer job queues with priority adjustments, or modeling simulations like . They are particularly useful in algorithms, like solving, where involves adding paths to one end and exploring from the other, or in mechanisms for text editors by treating operations as reversible additions to the front or back.

Introduction and Terminology

Definition and Basic Concept

A double-ended queue (deque) is an that supports the insertion and removal of elements from both its front and rear ends, providing flexibility beyond traditional linear structures. This structure generalizes both the , which follows a first-in, first-out () discipline by adding to the rear and removing from the front, and the , which adheres to a last-in, first-out (LIFO) order by operating solely at one end; a deque encompasses both behaviors as special cases by restricting operations to specific ends. It is pronounced /dɛk/, rhyming with "deck," to distinguish it from the verb "dequeue" associated with standard queues. For instance, a deque can emulate a traditional by exclusively inserting elements at the rear and removing them from the front, thereby maintaining FIFO ordering while retaining the capability for alternative usage patterns.

Naming Conventions

The preferred term for this data structure in is "deque," an abbreviation for "double-ended queue," pronounced like "deck." This was coined by E. J. Schweppe, as documented by Donald E. Knuth in his foundational text on algorithms. The term emerged in during the , reflecting early efforts to formalize versatile variants. To prevent ambiguity with the "dequeue," which describes removing an element from the front of a standard , the spelling "dequeue" for the data structure is generally deprecated in . Alternative designations include the expanded "double-ended queue" or, less frequently, "double-sided queue," though these are typically used for clarity in introductory contexts rather than formal references. In some older literature, shorthand forms like "deq" appear, but "deque" has become the convention. Endpoint designations vary slightly by source or implementation, with "front" and "back" preferred in many modern descriptions over "head" and "tail" to emphasize bidirectional access without overlap in terminology from unidirectional queues. For precision and consistency, "deque" is recommended in formal and academic discussions.

Variants and Distinctions

Sub-types of Deques

Deques can be restricted in their operations to create specialized variants that limit insertions or deletions to specific ends, offering trade-offs in flexibility for efficiency in certain applications. These restrictions distinguish them from full deques while retaining double-ended capabilities in a limited form. An input-restricted deque allows insertions only at one end, typically the rear, while permitting deletions from both the front and rear. This variant, also known as a queue-ended stack or "," supports enqueue operations exclusively at the rear but enables dequeue from either end, making it suitable for scenarios where input order is fixed but flexible output is needed. The concept originates from discussions in fundamental algorithms literature, where such restrictions simplify analysis of permissible sequences. Input-restricted deques have been applied in algorithms requiring ordered input with dual-output flexibility, such as Breu's algorithm for finding maximum values between moving pointers in a sequence, where the structure maintains invariants efficiently without full double-ended insertions. An output-restricted deque, conversely, permits insertions at both ends but restricts deletions to one end, usually the front. This allows enqueue from front or rear while limiting dequeue to the front, behaving somewhat like a stack for output but with queue-like input options. This variant is also known as a stack-ended queue or "steque." Like its input counterpart, it was formalized in early data structure analyses to explore permutation generation and sorting behaviors. They also support greedy strategies in deque-based computations, where deletions are confined to minimal elements to approximate optimal sequences. These sub-types bridge queues and stacks by relaxing one operation's restrictions while enforcing the other, enabling hybrid behaviors that queues (strict ) or stacks (LIFO) cannot provide without additional overhead. In modern computing, they are less prevalent than full deques in high-level languages but remain relevant in resource-constrained environments, such as systems or specialized algorithms, where operational limits reduce and .

Comparison with Other Data Structures

A double-ended queue, or deque, generalizes the data structure by permitting insertions and deletions at both the front and rear ends, whereas a standard enforces a strict first-in, first-out () discipline that allows insertions only at the rear and removals only from the front. Similarly, a deque extends the by supporting operations at both ends, in contrast to the stack's last-in, first-out (LIFO) restriction, which confines insertions and removals to a single top end. This dual-end flexibility positions the deque as a superset of both queues and stacks, enabling it to emulate their behaviors while offering additional versatility. Compared to singly or doubly linked lists, a deque maintains an ordered sequence with constant-time access and modifications at the ends but lacks efficient operations for accessing or modifying elements in the middle, requiring linear-time traversal from an endpoint. Linked lists, by contrast, support insertions and deletions throughout the structure in constant time given a direct pointer to the position, though locating arbitrary positions still demands O(n) traversal in the absence of indexing. Thus, deques prioritize end-focused operations on ordered collections, while linked lists provide broader positional flexibility at the cost of potentially higher overhead for end-specific tasks. In relation to dynamic arrays or vectors, deques enable efficient insertions and deletions at both ends without the need for element shifting, which would otherwise impose linear-time costs on front-end operations in a standard . Dynamic arrays excel at amortized constant-time appends to the rear and via indexing, but front insertions necessitate shifting all elements, degrading performance. Deques achieve their end efficiencies through specialized implementations, such as segmented arrays or doubly linked blocks, avoiding the contiguous storage pitfalls of dynamic arrays. Deques offer a balanced in flexibility, supporting a wider range of operations than specialized structures like queues or stacks, but they often incur higher constant factors due to the overhead of managing dual ends and potential non-contiguous storage. This generality comes at the expense of simplicity and optimization tailored to single-end access in queues or stacks, or full positional control in linked lists. Deques are particularly suitable for scenarios requiring hybrid behaviors, such as algorithms that alternate between queue-like FIFO processing and stack-like LIFO reversal, or applications like sliding window computations where elements are added or removed from either end dynamically.

Operations

Core Operations

A double-ended queue, commonly known as a deque, provides fundamental operations for adding, removing, and inspecting elements exclusively at its front and rear ends, enabling flexible and LIFO behaviors as needed. These operations form the abstract of a deque, independent of underlying representations. Insertion operations include push_front, which adds an to the front of the deque, and push_back, which adds an to the rear. For push_front, the high-level process involves checking if the deque is empty; if so, the new element becomes both the front and rear; otherwise, it is inserted immediately before the current front. Similarly, push_back checks for emptiness and sets the new element as both ends if empty, or appends it after the current rear if not. Removal operations are pop_front, which removes and returns the element at the front, and pop_back, which removes and returns the element at the rear. If the deque is empty, pop_front or pop_back typically throws an exception or returns a indicator to signal the error condition. After removal from a non-empty deque, the front or rear pointer updates to the next or previous element, respectively; if the deque becomes empty, both pointers may be set to . Inspection operations, peek_front and peek_back (also known as front and back), allow viewing the element at the front or rear without modifying the deque. These return the respective element if the deque is non-empty but throw an exception or return null if empty, mirroring the behavior of removal operations. The following illustrates these operations at an abstract level, assuming a structure with front and rear pointers and a for element insertion/removal: push_front(element):
if deque is empty:
    set front to new element
    set rear to new element
else:
    insert new element before front
    update front to new element
push_back(element):
if deque is empty:
    set front to new element
    set rear to new element
else:
    insert new element after rear
    update rear to new element
pop_front():
if deque is empty:
    throw EmptyDequeException or return [null](/page/Null)
else:
    temp = front
    update front to next element
    if front is now [null](/page/Null):
        set rear to [null](/page/Null)
    return temp
pop_back():
if deque is empty:
    throw EmptyDequeException or return null
else:
    temp = rear
    update rear to previous element
    if rear is now null:
        set front to null
    return temp
peek_front():
if deque is empty:
    throw EmptyDequeException or return null
return front element
peek_back():
if deque is empty:
    throw EmptyDequeException or return null
return rear element
These core operations assume sequential, single-threaded access; concurrent modifications require additional mechanisms for thread safety, which are beyond the standard abstract definition. In some deque variants, such as input-restricted or output-restricted types, certain operations may be prohibited, but the standard deque permits all four insertion and removal actions symmetrically.

Advanced Operations and Constraints

In addition to core insertion and removal operations at the ends, double-ended queues support supplementary functions for querying and managing their state. The operation returns the number of elements currently stored in the deque. The operation determines whether the deque contains any elements. The clear operation removes all elements from the deque, effectively emptying it. Some deque implementations provide capabilities, enabling direct retrieval of elements by position relative to either end. For example, an or such as at(n) or operator accesses the nth element from the front without bounds checking in the latter case. Deques also facilitate , allowing to elements from front to rear via forward iterators, or in reverse via reverse iterators. Descending iterators support traversal from rear to front in certain designs. Variants of deques impose constraints on permitted operations to suit specific applications. In an input-restricted deque, insertions are allowed only at one end—typically the rear—while deletions can occur from both ends. This restriction simplifies certain processing tasks, such as tree reductions, where elements are added sequentially but removed flexibly. An output-restricted deque, in contrast, permits insertions at both ends but limits deletions to one end, usually the front. Known in some contexts as a stack-ended or steque, this variant generalizes while enforcing output discipline. For instance, in an output-restricted deque, a pop from the rear is invalid, preventing removal from the insertion-only end opposite the front.

Implementations

Array-Based Implementations

Array-based implementations of double-ended queues utilize dynamic s, often configured as circular buffers, to support efficient insertions and deletions at both ends without frequent element shifting. This approach employs two pointers—one for the front and one for the rear—to track positions within the array, leveraging arithmetic to wrap around the array boundaries when necessary. In a circular buffer implementation, the is treated as a where indices are computed using the operator to simulate continuity. For instance, to perform a push_front operation, the front pointer is decremented by one and wrapped using the array size; if the buffer is full, the array is resized before insertion. The following illustrates a basic push_front for a bounded deque:
function push_front(element):
    if size == capacity:
        resize(capacity * 2)
    front = (front - 1 + capacity) % capacity
    array[front] = element
    size += 1
Similar logic applies to push_back by incrementing the rear pointer modulo capacity. To handle varying sizes efficiently, dynamic array resizing is employed: the capacity is typically doubled when the deque reaches full utilization to accommodate , and halved when the utilization falls below a (e.g., 25%) to reclaim memory. This resizing strategy relies on , ensuring that occasional O(n) copy operations are offset by multiple O(1) insertions, yielding constant average time per operation. These implementations offer advantages such as O(1) time complexity for random access to elements via direct indexing and improved cache locality due to contiguous memory storage. However, resizing can incur O(n) time in the worst case, though amortized to O(1), and may lead to temporary memory overhead during reallocation. A notable example is the C++ standard library's std::deque, which enhances the basic array approach by using an array of fixed-size blocks (typically 512 bytes each) pointed to by a dynamic map array. This chunked structure balances efficient end operations with reasonable random access, minimizing large-scale shifts during insertions or deletions at the ends.

Linked List Implementations

Doubly linked list implementations of deques utilize a of , where each contains the along with pointers to the previous and next , enabling efficient navigation and modification from both ends. To facilitate boundary operations and avoid special cases for empty lists, —such as a header (pointing to the front) and a trailer (pointing to the rear)—are commonly employed at the beginning and end of the list. This structure maintains references to the head and tail for direct access, ensuring that insertions and deletions at either end involve only constant-time pointer adjustments. The core operations, such as push_front and push_back, exemplify the simplicity of this approach. For push_front(value), a new is created with the given value; its next pointer is set to the current head (or the node after the header ), and the current head's previous pointer is updated to point back to the new ; finally, the head is updated to the new . If the deque was empty, the new 's previous pointer points to the header, and its next to the trailer. for this operation is as follows:
function push_front(value):
    new_node = new [Node](/page/Node)(value)
    new_node.next = head
    if head is not null:
        head.prev = new_node
    head = new_node
    if head.next is null:  // was empty
        tail = head
    new_node.prev = null  // or header [sentinel](/page/Sentinel)
Similar pointer manipulations apply to push_back, pop_front, and pop_back, each achieving O(1) without amortization. Unlike array-based implementations that require resizing, deques grow and shrink dynamically by allocating or freeing individual nodes as needed, eliminating the need for or preallocation. This results in no wasted space for unused slots, with overall of where n is the number of elements. The primary advantages include true worst-case O(1) time for all end operations, as pointer updates are independent of list size, and efficient handling of dynamic sizes without contiguous memory constraints. However, disadvantages arise from non-contiguous memory allocation, leading to poor cache locality during sequential traversals, and the absence of efficient —reaching the i-th element requires O(n) traversal time from either end. An optimized real-world example is Python's collections.deque, which employs a of fixed-size blocks (typically 64 elements each) rather than single-element nodes; this hybrid reduces pointer overhead and improves cache performance while preserving O(1) end operations.

Purely Functional Implementations

In purely functional implementations of double-ended queues (deques), the structures are persistent, such that every operation—such as insertion or deletion at either end—returns a new deque while sharing the unmodified portions of the original structure to minimize space and time overhead through structural sharing. This approach aligns with the immutability principles of languages, avoiding in-place mutations and enabling safe concurrent access without locks. A foundational technique is the Banker's deque, which represents the deque using two balanced streams: a front for efficient head access and a rear (often reversed) for tail access, maintaining the invariant that the front is at least as large as the rear to amortize costs. When the balance is violated, such as after multiple rear insertions, a lazily rebuilds the front by appending the reversed rear, distributing the O(n) rebuilding cost over subsequent operations to achieve amortized O(1) time for , head, , snoc, last, and . This method, analyzed using the banker's approach to potential functions, ensures linear total time for sequences of operations. Earlier work by and Melville introduced the first purely functional real-time queue using global rebuilding, where the structure is periodically reconstructed from scratch to maintain balance, and this was extended to deques with similar guarantees for worst-case O(1) operations without amortization. Lazy rebuilding simplifies global rebuilding by deferring reconstruction until an imbalance is detected during access, leveraging to incrementally compute changes and yielding amortized O(1) bounds while reducing implementation complexity. For variants without laziness, Okasaki developed scheduling mechanisms that force a constant number of suspensions per operation, ensuring worst-case O(1) time for all core deque operations by proactively managing computation steps.90030-2) A prominent example is Haskell's Data.Sequence, implemented in the containers library using 2-3 finger trees annotated with sizes, which support amortized O(1) access to both ends and O(log n) splits and concatenations, generalizing deques to versatile sequences suitable for functional contexts. These implementations offer key advantages, including inherent due to immutability and compatibility with pure functional paradigms, where multiple versions of a deque can coexist without interference, facilitating reasoning about programs and enabling efficient parallelism.

Language and Library Support

Built-in Language Support

In C++, the provides std::deque as a built-in container in the <deque> header, implementing a double-ended queue that supports efficient insertion and deletion at both ends via methods such as push_front and push_back, along with constant-time to elements by index. For example, the following code demonstrates basic usage:
cpp
#include <deque>
#include <iostream>

int main() {
    std::deque<int> dq;
    dq.push_back(1);  // Add to end
    dq.push_front(0); // Add to front
    std::cout << dq[0] << std::endl; // Access by index
    return 0;
}
In Java, the java.util.Deque interface, introduced in Java 1.6, defines the double-ended queue operations including addFirst, addLast, removeFirst, and removeLast, with the array-based ArrayDeque class serving as the primary built-in implementation that provides resizable storage without capacity restrictions. An example usage is:
java
import java.util.ArrayDeque;
import java.util.Deque;

public class Example {
    public static void main(String[] args) {
        Deque<Integer> dq = new ArrayDeque<>();
        dq.addFirst(0); // Add to front
        dq.addLast(1);  // Add to end
        System.out.println(dq.removeFirst()); // Remove from front
    }
}
Python includes collections.deque as a built-in type since version 2.4, offering thread-safe append and pop operations from both ends with methods like appendleft and append, as well as a rotate method for efficient shifting of elements. A simple example:
python
from collections import deque

dq = deque()
dq.appendleft(0)  # Add to left (front)
dq.append(1)      # Add to right (end)
print(dq.popleft())  # Remove from left
Rust's standard library features std::collections::VecDeque, a double-ended queue built on a growable ring buffer for efficient operations at both ends, supporting push_front, push_back, pop_front, and pop_back. Example code:
rust
use std::collections::VecDeque;

fn main() {
    let mut dq: VecDeque<i32> = VecDeque::new();
    dq.push_front(0); // Add to front
    dq.push_back(1);  // Add to back
    println!("{}", dq.pop_front().unwrap()); // Remove from front
}
Ada provides support through the Ada.Containers.Doubly_Linked_Lists package in the standard library since Ada 2005, which, while primarily a doubly-linked list, can be adapted for deque functionality using operations like Prepend and Append to insert at the beginning or end, and Delete_First and Delete_Last for removal. For instance:
ada
with Ada.Containers.Doubly_Linked_Lists;
with Ada.Text_IO; use Ada.Text_IO;

procedure Deque_Example is
   package Int_Lists is new Ada.Containers.Doubly_Linked_Lists (Element_Type => [Integer](/page/Integer));
   use Int_Lists;
   L : [List](/page/List) := Empty_List;
begin
   Prepend (Container => L, New_Item => 0); -- Add to front
   Append (Container => L, New_Item => 1);  -- Add to end
   Delete_First (Container => L);           -- Remove from front
end Deque_Example;
Historically, early dialects supported deque-like structures through their fundamental list primitives, such as cons cells, which enabled efficient implementations of double-ended operations in symbolic processing tasks central to the language's design since the late .

Third-Party and Standard Library Implementations

In the Go programming language, third-party libraries fill the gap for deque support, as the provides only a doubly-linked list via container/list. A widely used implementation is the deque package from user gammazero, which offers a high-performance ring-buffer-based deque optimized for efficient insertions and removals at both ends, with O(1) amortized for core operations. This package has been available since 2017 and supports types in modern Go versions, making it suitable for concurrent applications. Swift's standard library lacks a built-in deque, but the official Swift Collections package, developed by Apple and released in , includes the Deque type as part of its multiplatform collection enhancements. This implementation uses a ring buffer backend for random-access efficiency, supporting O(1) insertions and deletions at either end while maintaining array-like subscripting and range-replaceability. As of 2025, it remains a core component of the package, with updates in version 1.3.0 improving integration with frameworks. JavaScript, lacking a native deque in its standard library, relies on third-party modules for robust implementations. The deque library by petkaantonov provides an extremely fast, low-level double-ended queue using a contiguous with modular indexing, achieving near-optimal performance for operations at both ends without garbage collection overhead in environments. Complementing this, Immutable.js's List structure implements deque semantics with full persistence, enabling O(log N) updates and efficient reversal or slicing, ideal for paradigms. Another option is @datastructures-js/deque, a modern ES6-compatible package emphasizing and benchmarked performance comparable to native s for small to medium sizes. In C, where the standard library offers no deque facilities, developers often resort to custom implementations using dynamic arrays or doubly-linked lists, or third-party options like the dque library from c-utils, which emulates the C++ std::deque API with support for LIFO, FIFO, and variants through a generic key-based structure. This library, designed for embedded and , ensures portability and minimal dependencies. Julia's ecosystem provides the Deque type via the DataStructures.jl package, an established third-party for advanced data structures. Implemented as an of blocks, it delivers amortized O(1) time for end insertions and removals, with efficient iteration and resizing for scientific workloads. By , Kotlin has expanded deque support beyond its standard ArrayDeque through libraries like kotlinx.collections.immutable, which introduces persistent, immutable collection interfaces suitable for concurrent and multiplatform applications. While not a direct deque, its PersistentQueue and related structures enable deque-like behaviors with thread-safe persistence, addressing needs in reactive and coroutine-based programming.

Performance Characteristics

Time Complexity

The time complexity of double-ended queue (deque) operations varies depending on the underlying implementation, such as doubly-linked lists or array-based structures, with Big O notation providing an asymptotic analysis of efficiency. In doubly-linked list implementations, insertions and deletions at both ends—such as push_front, push_back, pop_front, and pop_back—achieve O(1) worst-case time complexity, as these operations only require updating a constant number of pointers without traversing the structure. In contrast, array-based implementations, including circular buffers, typically offer O(1) amortized time for these end operations, accounting for occasional resizing costs when the array capacity is exceeded. Random access to elements by is O(1) in array-based deques, benefiting from direct indexing, whereas it requires time in implementations due to the need to traverse from the head or . Insertions and deletions in the middle of a deque incur across both array-based and implementations, as they necessitate shifting or traversing a linear number of elements to maintain the structure. Resizing in array-based deques, often triggered by operations like push_front or push_back when capacity is full, results in amortized O(1) time per operation through methods such as the or accounting argument, where infrequent O(n) rebuilds (e.g., copying elements to a doubled ) are spread across many O(1) steps. For instance, in an array-based push_front, the occasional O(n) rebuild occurs only when resizing, but the average cost per insertion remains O(1) over a sequence of operations. In hybrid implementations, such as those using fixed-size blocks (e.g., Python's collections.deque with blocks of 64 elements), the choice of block size influences constant factors in time but preserves the O(1) amortized bound for end operations by distributing resizing overhead.
OperationLinked List (Worst-Case)Array-Based (Amortized)
Push/Pop Front/BackO(1)O(1)
Random AccessO(n)O(1)
Insert/Delete MiddleO(n)O(n)
This table summarizes the key time complexities for primary deque operations across implementations.

Space Complexity

The of a double-ended queue (deque) is generally , where n is the number of elements stored, across various implementations, as the structure must allocate proportional to the elements it holds. This includes the space for the elements themselves plus for navigation and management. The total space can be expressed as S = n \times e + m, where e is the size of each element and m is the metadata overhead (such as pointers or indices). In a implementation, the deque achieves exact space usage, with each typically requiring storage for the plus three pointers: one for the previous , one for the next , and potentially a reference to the data if not embedded. On a 64-bit system, the pointer overhead alone contributes approximately 24 bytes per (three 8-byte pointers), making this approach memory-intensive for small elements due to the fixed per-node cost, though it avoids pre-allocation waste. Array-based implementations also exhibit space complexity but incur additional capacity overhead to support efficient insertions without frequent reallocations. These typically over-allocate storage, such as doubling the capacity (growth factor of 2) upon reaching limits, leading to temporary unused space that averages around 50% immediately after resizing in unbalanced workloads. For example, in Java's ArrayDeque, the structure grows dynamically without fixed restrictions, but the buffer may hold up to twice the current size post-expansion, balancing amortized time efficiency against memory utilization. Circular buffer variants within array deques minimize shifting but can waste up to 50% space during resizes if the new allocation is not immediately filled. Purely functional implementations maintain O(n) space complexity when balanced, using structures like streams or suspended lists to represent front and rear segments, with metadata such as length fields adding constant overhead per segment. Unbalanced variants, such as simple cons-based rear additions without rotation, can lead to O(n log n) worst-case space in persistent contexts due to accumulated suspensions or deep nesting, though balancing techniques like periodic rotation restore O(n) usage. Chris Okasaki's Banker's deques, for instance, enforce invariants like |F| ≥ |R| to ensure linear space via lazy evaluation. Optimizations like block-based designs reduce pointer overhead in practice; for example, Python's collections.deque employs a doubly-linked list of fixed-size blocks (each holding multiple elements contiguously), lowering the effective cost compared to per-element pointers while preserving O(1) end operations. This hybrid approach achieves near-array locality with linked flexibility, making it suitable for large n where pure linked lists would fragment memory excessively.

Applications

Algorithmic Use Cases

Double-ended queues (deques) are particularly valuable in algorithmic contexts where efficient insertions and deletions from both ends enable optimized solutions to problems involving dynamic windows, symmetric comparisons, or prioritized traversals. Their ability to maintain order while supporting O(1) operations at either end makes them ideal for scenarios where stacks or queues alone would require additional overhead or restructuring. One prominent algorithmic is the sliding maximum problem, where the goal is to find the maximum element in every contiguous subarray of fixed size k within an of n elements. A naive approach would examine each in O(k) time, leading to O(nk) overall complexity, but a deque-based achieves time by maintaining a deque of indices corresponding to potentially useful elements in decreasing order of their values. As the slides, indices of elements outside the current window are removed from the front, and from the back, any indices with values smaller than the new element are discarded since they cannot be maxima in future windows. The front of the deque always holds the index of the current maximum, allowing O(1) retrieval per . The following pseudocode illustrates the deque maintenance for the sliding window maximum:
function slidingWindowMaximum(arr, k):
    deque = empty deque  // stores indices
    result = empty array
    
    for i from 0 to length(arr)-1:
        // Remove indices outside current window
        if deque not empty and deque.front() == i - k:
            deque.pop_front()
        
        // Remove indices with smaller values from back
        while deque not empty and arr[deque.back()] < arr[i]:
            deque.pop_back()
        
        deque.push_back(i)
        
        // After first k-1 elements, start collecting results
        if i >= k - 1:
            result.append(arr[deque.front()])
    
    return result
This approach ensures each element is added and removed from the deque at most once, confirming the O(n) time complexity. Deques also facilitate efficient palindrome checking for strings or sequences, leveraging symmetric access to compare characters from both ends without reversing the structure. The algorithm enqueues all characters into the deque, then iteratively dequeues from the front and rear, verifying if they match; if any pair mismatches, the sequence is not a palindrome. This process runs in O(n) time and space, where n is the length, and demonstrates the deque's utility in problems requiring bidirectional symmetry. In graph algorithms, deques enhance (BFS) variants, particularly 0-1 BFS for finding shortest paths in unweighted or 0-1 weighted , such as grid-based mazes where moves in certain directions incur zero or unit cost. Unlike standard BFS with a , 0-1 BFS treats the deque as a priority mechanism: nodes reached via cost-0 edges are pushed to the front (simulating zero increase in distance), while cost-1 edges push to the back, ensuring Dijkstra-like optimality in O(V + E) time without a full . This is especially effective in grid paths where alternating directions might correspond to different costs, maintaining level-like exploration while prioritizing cheaper paths. Deques further support undo/redo buffers in algorithmic simulations of history management, such as in text editors or command sequences, by pushing new states to one end for the forward and popping from the same end for , while transferring popped elements to the opposite end for redo operations. This bidirectional management avoids the need for separate stacks, enabling efficient navigation through action histories with O(1) operations per action, bounded by the deque's capacity to prevent unbounded growth.

Real-World and Emerging Applications

Double-ended queues (deques) play a crucial role in work-stealing algorithms for , enabling efficient load balancing across threads. In (TBB), each thread maintains a local deque of tasks, allowing workers to push tasks onto one end and steal from the other end of threads' deques to minimize overhead and improve throughput in multicore environments. Similarly, Java's ForkJoinPool employs per-worker deques to implement work-stealing, where forked tasks are pushed to the and joined from the head, with threads stealing from the of busy workers' deques to balance computational load dynamically. In applications, deques facilitate history management for forward and backward . Web often use a deque to store visited URLs, adding new pages to the front upon and removing from the back or front for back/forward operations, ensuring efficient access to recent history while maintaining a bounded size to limit memory usage. For concurrent programming, lock-free deques enable efficient multithreaded operations without traditional locks, reducing contention in high-throughput scenarios. Rust's crossbeam-deque library provides a work-stealing deque implementation using atomic operations and epoch-based reclamation, allowing safe from both ends across threads for building scalable schedulers and runtimes.

Historical Development

Origins of the Concept

The of the double-ended (deque) evolved from early generalizations of structures used in computer simulations during the , where queues modeled waiting lines in and event-driven systems. The term "deque" was coined in 1961 by E. J. Schweppe in the context of , referring to a structure permitting insertions and deletions at both ends, as documented and analyzed by Donald E. Knuth in his foundational text on algorithms. In his 1968 book The Art of Computer Programming, Volume 1: Fundamental Algorithms, Donald E. Knuth introduced restricted deques—variants limiting operations to one end for insertion or deletion—as useful in various algorithmic contexts, including parsing. The deque's theoretical significance was further highlighted in its relation to automata theory, where a two-way finite automaton equipped with a deque as auxiliary storage achieves computational power equivalent to a Turing machine, bridging regular languages with full recursively enumerable sets through bidirectional access to the storage medium.

Key Publications and Evolutions

In 1995, Chris Okasaki published "Simple and Efficient Purely Functional Queues and Deques" in the Journal of Functional Programming, introducing amortized O(1) implementations of deques using and scheduling techniques tailored for purely functional languages. This work addressed the challenges of maintaining efficiency in immutable settings by balancing front and rear lists through incremental rebuilding. The third edition of The Art of Computer Programming, Volume 1: Fundamental Algorithms (1997) by Donald E. Knuth updated the analysis of deques as a key abstract data type within linear lists, emphasizing their utility in algorithms for sorting and searching through balanced representations and operations on both ends. Knuth's analysis provided rigorous bounds on time and space complexities, establishing deques as foundational for sequential data processing. Building on these foundations, Haim Kaplan and Robert E. Tarjan advanced the field in 1999 with their paper "Purely Functional, Deques with " in the Journal of the Association for Computing Machinery. The authors developed worst-case O(1) deques using lazy rebuilding and recursive slowdown, enabling efficient and supporting persistent variants without amortization. The 2000s marked a shift toward concurrent deques to meet demands in parallel and multiprocessor systems, extending earlier non-blocking queue designs. For instance, Maged M. Michael's 2003 paper "CAS-Based Lock-Free Algorithm for Shared Deques" presented the first practical lock-free deque using compare-and-swap operations, achieving linearizability and scalability for multi-threaded environments. This built on his prior work with Michael L. Scott on concurrent queues, adapting techniques like pointer swinging for bidirectional access. In 2000, David L. Detlefs, Christine H. Flood, Alexander T. Garthwaite, Paul A. Martin, Nir Shavit, and Guy L. Steele Jr. improved DCAS-based concurrent deques in "Even Better DCAS-Based Concurrent Deques," reducing complexity while preserving non-blocking progress. These key publications have profoundly influenced deque theory, enabling amortized and worst-case efficient structures in and lock-free concurrency for . Recent work, such as verified implementations of catenable deques (as of ), continues to build on these foundations.

References

  1. [1]
    [PDF] Chapter 7: Queues and Deques
    The fundamental property of the queue is that items are inserted at one end (the rear of the line) and removed from the other (the door to the theater). This ...
  2. [2]
    [PDF] Stacks, Queues, Deques, and Steques Fall 2010 R. Tarjan, Preceptor
    Both of these are restrictions of a more general data structure, the double-ended queue, or deque (terminology suggested by Knuth, pronounced to rhyme with deck) ...
  3. [3]
    [PDF] Page 1 of 10 3.3 The Queue ADT We will look at the queue as our ...
    3.4.2 Applications. A deque can be used as a queue or a stack. It can also be used in problem solving: Consider finding a path through a maze. As the path is ...
  4. [4]
    [PDF] CMSC 420: Lecture 4 Some Basic Data Structures - UMD CS
    Deque: This data structure is a combination of stacks and queues, called a double-ended queue or deque for short. It supports insertions and removals from ...
  5. [5]
    [PDF] Chapter 5 Stacks, Queues, and Deques
    Jan 13, 2011 · Such an extension of a queue is called a double- ended queue, or deque, which is usually pronounced “deck” to avoid confusion with the dequeue ...
  6. [6]
    [PDF] Data Structures and Algorithms in C++ 2e - eduarmandov – IT
    ended queue, or deque, which is usually pronounced “deck” to avoid confusion with the dequeue function of the regular queue ADT, which is pronounced like.
  7. [7]
    [PDF] 2 Week 2: February 5
    Feb 5, 2024 · • An output-restricted deque or stack-ended queue ... • Am input-restricted deque or queue-ended stack or quack supports deletions at both.
  8. [8]
    [PDF] Lecture 2: CMSC 420
    input-restricted deque: enqueue only one end output-restricted deque: dequeue only one end. Special cases of List ADT: restrict insert, delete, and get to a.
  9. [9]
    Deque Operations: The Swiss Army Knife of Data Structures
    Buffer Management: Handle data streams in networking applications. Game ... Output-Restricted Deque. Here, you can insert at both ends but can only ...
  10. [10]
    [PDF] Greedy Is an Almost Optimal Deque - Computer Science
    Definition 23 (Output-restricted). A deque sequence is output-restricted if it has dele- tions only at minimum elements. Theorem 24. The cost of executing a ...
  11. [11]
    10.1 Stacks and queues - CLRS Solutions
    ... queue allows insertion at one end and deletion at the other end, a deque (double-ended queue) allows insertion and deletion at both ends. Write four O ( 1 ) ...
  12. [12]
    LinkedLists
    The reason is that unlike an array, a linked list scatters its contents ... It is a bit trickier to build a queue out of an array than to build a stack.
  13. [13]
    [PDF] STACKS, QUEUES, AND LINKED LISTS - Purdue Computer Science
    A double-ended queue, or deque, supports insertion and deletion from the front and back. • The Deque Abstract Data Type. - insertFirst(e): Insert e at the ...
  14. [14]
    [PDF] Algorithms and data structures for graph analysis - Stanford Network ...
    This project aims to. Page 3. assess the trade-offs between data structures for graph applications, with a bias towards applications for larger data sets.
  15. [15]
    [PDF] Data Structures and Algorithm Analysis - People
    Jun 5, 2012 · ... introduction to algorithms, their design, and their analysis, see Introduction to Al- gorithms: A Creative Approach by Udi Manber [Man89] ...
  16. [16]
    [PDF] CSE373: Data Structures and Algorithms Lecture 1: Introduction; ADTs
    Trade-offs. A data structure strives to provide many useful, efficient operations. But there are unavoidable trade-offs: – Time vs space. – One operation's ...
  17. [17]
    [PDF] CMSC 420: Lecture 2 Some Basic Data Structures
    To a large extent, this course will be concerned with the various approaches for implementing simple abstract data types and the tradeoffs between these options ...Missing: trade- | Show results with:trade-
  18. [18]
    [PDF] Introduction, amortization, arrays, stacks, queues, and deques
    Pronounced “deck”, stands for “double-ended queue”. Page 44. Dequeues and queues: Layout and Operations. Store as contiguous block of cells in a dynamic array ...<|control11|><|separator|>
  19. [19]
  20. [20]
    Deque (Java Platform SE 8 )
    ### Summary of Deque Operations (Java SE 8)
  21. [21]
    [PDF] Fundamentals of Programming 2 - Queues and Their Applications
    Mar 16, 2020 · Double-ended Queue. Aside from the fifo queues there also exist double-ended queues or deques for which the operations of adding and removing ...
  22. [22]
    The reduction of binary trees by means of an input-restricted deque
    249-284 · Suivant. no. 3. The reduction of binary trees by means of an input-restricted deque ... D. E. Knuth and S. O. Rice, The Average Height of Planted ...
  23. [23]
    1.3 Bags, Queues, and Stacks - Algorithms, 4th Edition
    Feb 13, 2020 · A stack-ended queue or steque is a data type that supports push, pop, and enqueue. Knuth calls it an output-restricted deque. Implement it using ...<|control11|><|separator|>
  24. [24]
    Implement a Deque Using an Array - HappyCoders.eu
    Rating 5.0 (20) Jun 7, 2022 · In this part of the tutorial series, I will show you how to implement a deque using an array – more precisely: with a circular array.Implementing a Bounded... · Source Code for the Bounded...
  25. [25]
  26. [26]
    Inside STL: The deque, implementation -- Raymond Chen
    Sep 20, 2023 · All three of the major implementations of the standard library maintain an array of pointers to blocks, which they confusingly call a “map” even ...Missing: SGI | Show results with:SGI
  27. [27]
    Linked List vs Array - GeeksforGeeks
    Jul 23, 2025 · Advantages of Linked List over arrays : · Efficient insertion and deletion · Implementation of Queue and Deque · Space Efficient in Some Cases : ...
  28. [28]
    File not found · python/cpython
    Insufficient relevant content. The provided content is a GitHub "File not found" page, which does not contain the source code or description of the deque implementation from `dequeobject.c`. No information about the deque implementation, such as whether it is a doubly-linked list of blocks or details about nodes with prev/next, is available in the content.
  29. [29]
    [PDF] Purely Functional Data Structures - CMU School of Computer Science
    Figure 5.2: An implementation of deques based on lazy rebuilding and the banker's method. ... purely functional implementation of real-time deques. A.
  30. [30]
    Data.Sequence - Hackage - Haskell.org
    Implementation. The implementation uses 2-3 finger trees annotated with sizes, as described in section 4.2 of. Ralf Hinze and Ross Paterson, "Finger trees: a ...
  31. [31]
    [PDF] Finger trees: a simple general-purpose data structure
    Finger trees are a functional, persistent sequence data structure with efficient access to ends, and can be used as a sequence, priority queue, search tree, ...
  32. [32]
    ArrayDeque (Java SE 17 & JDK 17) - Oracle Help Center
    ArrayDeque is a resizable-array implementation of the Deque interface, with no capacity restrictions, and is not thread-safe. It grows as needed.
  33. [33]
    What's New in Python 2.4 — Python 3.14.0 documentation
    Several modules, such as the Queue and threading modules, now take advantage of collections.deque for improved performance. (Contributed by Raymond Hettinger.).What's New In Python 2.4 · Pep 318: Decorators For... · Pep 327: Decimal Data Type
  34. [34]
    collections — Container datatypes — Python 3.14.0 documentation
    The rotate() method provides a way to implement deque slicing and deletion. For example, a pure Python implementation of del d[n] relies on the rotate() method ...Chainmap Objects · Deque Objects · Namedtuple() Factory...
  35. [35]
    VecDeque in std::collections - Rust
    A double-ended queue implemented with a growable ring buffer. The “default” usage of this type as a queue is to use push_back to add to the queue, ...
  36. [36]
    A.18.3 The Package Containers.Doubly_Linked_Lists
    A doubly-linked list container object manages a linked list of internal nodes, each of which contains an element and pointers to the next (successor) and ...Missing: deque documentation
  37. [37]
    [PDF] History of Lisp - John McCarthy
    Feb 12, 1979 · This paper concentrates on the development of the basic ideas and distin- guishes two periods - Summer 1956 through Summer 1958 when most of ...<|control11|><|separator|>
  38. [38]
    deque package - github.com/gammazero/deque - Go Packages
    Package deque provides a fast ring-buffer deque (double-ended queue) implementation. Deque generalizes a queue and a stack, to efficiently add and remove items ...
  39. [39]
    apple/swift-collections: Commonly used data structures for ... - GitHub
    Deque<Element> , a double-ended queue backed by a ring buffer. Deques are range-replaceable, mutable, random-access collections. Heap , a min-max heap backed by ...
  40. [40]
    Swift Collections 1.3.0
    Sep 29, 2025 · Swift Collections 1.3.0 is out now; it is a new feature release primarily focused on an initial set of basic constructs that help Swift's ...Should we rename Deque? - Page 3 - Collections - Swift ForumsShould we rename Deque? - Page 2 - Collections - Swift ForumsMore results from forums.swift.org<|separator|>
  41. [41]
    petkaantonov/deque: Extremely fast double-ended queue ... - GitHub
    Extremely fast double-ended queue implementation. Contribute to petkaantonov/deque development by creating an account on GitHub.
  42. [42]
    List - Immutable.js
    Lists are immutable and fully persistent with O(log32 N) gets and sets, and O(1) push and pop. Lists implement Deque, with efficient addition and removal.
  43. [43]
    @datastructures-js/deque - npm
    Aug 27, 2025 · A performant double-ended queue (deque) implementation in javascript.. Latest version: 1.0.6, last published: 4 days ago.
  44. [44]
    c-utils/dque: Double-ended queue library with API similar to ... - GitHub
    Double-ended queue library with API similar to C++ deque modified for C. Can be used for LIFO, FIFO, and Priority queues using any "key".
  45. [45]
    Kotlin/kotlinx.collections.immutable - GitHub
    Immutable collection interfaces and implementation prototypes for Kotlin. This is a multiplatform library providing implementations for jvm, js, wasmJs, ...What's In This Library · Interfaces And... · OperationsMissing: deque | Show results with:deque
  46. [46]
    Big-O Notation of Stacks, Queues, Deques, and Sets - Baeldung
    Mar 18, 2024 · In this tutorial, we'll explain the complexities of operations on the main data structures like stacks, queues, deques, and sets.
  47. [47]
    Introduction to the Java ArrayDeque | Baeldung
    ### Time Complexities for ArrayDeque Operations in Java
  48. [48]
    TimeComplexity - Python Wiki
    Jan 19, 2023 · This page documents the time-complexity (aka "Big O" or "Big Oh") of various operations in current CPython.<|control11|><|separator|>
  49. [49]
    Doubly Linked List Tutorial - GeeksforGeeks
    Sep 19, 2025 · The main advantage of a doubly linked list is that it allows for efficient traversal of the list in both directions. This is because each node ...Difference between Singly... · Insertion in a Doubly Linked List
  50. [50]
    What is a Memory-Efficient Doubly Linked List in C? - Stack Overflow
    Mar 7, 2016 · If you're worried about memory, it's almost always better to use a doubly-linked list with more than 1 element per node, like a linked list of ...Why is deque implemented as a linked list instead of a circular array?Deque implementation using doubly linked listed - Stack OverflowMore results from stackoverflow.comMissing: deque | Show results with:deque
  51. [51]
    ArrayDeque (Java SE 20 & JDK 20) - Oracle Help Center
    Array deques have no capacity restrictions; they grow as necessary to support usage. They are not thread-safe; in the absence of external synchronization, they ...Missing: overhead | Show results with:overhead
  52. [52]
    Performance of ArrayList vs ArrayDeque while implementing stack
    May 1, 2015 · ArrayDeque is resized by factor 2, while ArrayList is 1.5 · The initial capacity of ArrayDeque is 16, while ArrayList is 10.Does ArrayDeque have overhead of shifting elements on remove/add?c++ - How does deque have an amortized constant Time ComplexityMore results from stackoverflow.comMissing: overhead | Show results with:overhead
  53. [53]
    Implementation of Circular Queue Using Array - GeeksforGeeks
    Oct 30, 2025 · In a normal array implementation, dequeue() can be O(n) or we may waste space. Using a circular array, both enqueue() and dequeue() can be done ...
  54. [54]
    collections — Container datatypes
    ### Summary of Python's `collections.deque` Implementation
  55. [55]
    How are deques in Python implemented, and when are they worse ...
    Jun 6, 2011 · A dequeobject is composed of a doubly-linked list of block nodes. So yes, a deque is a (doubly-)linked list as another answer suggests.Why is deque implemented as a linked list instead of a circular array?Is collections.deque() the best implementation of constant length list ...More results from stackoverflow.com
  56. [56]
    Most Commonly Asked Data Structure Interview Questions on Deque
    Sep 7, 2025 · 8. How would you use a deque to check for palindromes efficiently? · Push all characters into the deque (addRear). · Repeatedly compare and remove ...<|control11|><|separator|>
  57. [57]
    Sliding Window Maximum (Maximum of all subarrays of size K)
    Aug 13, 2025 · Run a loop and insert the first k elements in the deque. Before inserting the element, check if the element at the back of the queue is smaller ...
  58. [58]
    Sliding Window Maximum - LeetCode
    Can you solve this real interview question? Sliding Window Maximum - You are given an array of integers nums, there is a sliding window of size k which is ...
  59. [59]
    Sliding Window Maximum - InterviewBit
    Oct 13, 2021 · Efficient Approach: Using Deque · Consider only the indices of the elements in the current window. · Pop-out all the indices of elements smaller ...<|control11|><|separator|>
  60. [60]
    0-1 BFS - Algorithms for Competitive Programming
    Sep 10, 2025 · In this article we demonstrate how we can use BFS to solve the SSSP (single-source shortest path) problem in O ( | E | )
  61. [61]
    Shortest Paths with Unweighted Edges - USACO Guide
    A 0/1 BFS finds the shortest path in a graph where the weights on the edges can only be 0 or 1, and runs in O(V+E) using a deque.
  62. [62]
    Python's deque: Implement Efficient Queues and Stacks
    Python's deque is a double-ended queue designed for fast, memory-efficient append and pop operations on both ends, useful for implementing queues and stacks.
  63. [63]
    Implement Undo and Redo - GeeksforGeeks
    Sep 15, 2025 · The idea is to maintain the character whenever we perform an undo(). Remove the last character from the current string and push it onto the ...<|control11|><|separator|>
  64. [64]
    3.4 Task Parallel and Work Stealing Models - Fiveable
    Deques (double-ended queues) manage task distribution in work stealing algorithms ... work stealing include Cilk and Intel Threading Building Blocks (TBB). Work ...Task Parallelism And Work... · Designing Parallel... · Performance Of Task Parallel...
  65. [65]
    Accelerating Real-Time Applications with Predictable Work-Stealing
    Internally, TBB [8, 14] resembles mostly classic work-stealing for scheduling tasks, with decentralized LiFo deques and randomized stealing. It uses a ...State Of Task-Based... · Resource-Trading Algorithm · Performance Analysis<|separator|>
  66. [66]
    Guide to Work Stealing in Java | Baeldung
    Jun 11, 2024 · We can create a work-stealing thread pool using either the ForkJoinPool class or the Executors class: ForkJoinPool commonPool = ForkJoinPool.
  67. [67]
    Work-Stealing & Recursive Partitioning with Fork/Join - igvita.com
    Feb 29, 2012 · JDK7's Fork/Join combines a double ended queue (deque) and a recursive job partitioning step to minimize synchronization - a great design ...
  68. [68]
    Applications, Advantages and Disadvantages of Deque
    Jul 23, 2025 · In a web browser's history, recently visited URLs are added to the front of the deque and the URL at the back of the deque is removed after some ...
  69. [69]
    Deque (Double-Ended Queue) Applications - HeyCoach
    Applications of Deques · 1. Palindrome Checker · 2. Undo Functionality in Applications · 3. Sliding Window Problems · 4. Task Scheduling · 5. Browser History ...Applications Of Deques · 1. Palindrome Checker · 3. Sliding Window Problems<|separator|>
  70. [70]
    Understanding Queues, Deques, and Circular Queues in Data ...
    Oct 21, 2025 · ... buffering in data streams, and other fixed-size ring buffers. The deque is used to store the indices of array elements in the current window ...
  71. [71]
    crossbeam-rs/crossbeam: Tools for concurrent programming in Rust
    crossbeam-skiplist provides concurrent maps and sets based on lock-free skip lists. Usage. Add this to your Cargo.toml : [dependencies] crossbeam = "0.8" ...
  72. [72]
    A Novel Real-Time Data Stream Transfer System in Edge ... - MDPI
    Smart logistics systems generate massive amounts of data, such as images and videos, requiring real-time processing in edge clusters.Missing: double- | Show results with:double-
  73. [73]
    Trends in Data Organization and Access Methods
    ... Hardware,developments,have,made,available,many,possibilities,for ... double-ended,queue,and,may,occur,in,the,form,of,output-restricted,or,input ...
  74. [74]
    The art of computer programming, volume 1 (3rd ed.)
    The art of computer programming, volume 1 (3rd ed.): ... Atchison W, Conte S, Hamblen J, Hull T, Keenan T, Kehl W, McCluskey E, Navarro S, Rheinboldt W, Schweppe ...
  75. [75]
    Simple and efficient purely functional queues and deques
    Simple and efficient purely functional queues and deques. Part of: JFP Research Articles. Published online by Cambridge University Press: 07 November 2008.
  76. [76]
    Art of Computer Programming, The: Volume 1 - InformIT
    30-day returnsArt of Computer Programming, The: Volume 1: Fundamental Algorithms, 3rd Edition. By Donald E. Knuth; Published Jul 7, 1997 by Addison-Wesley Professional.
  77. [77]
    Purely functional, real-time deques with catenation
    KAPLAN, H., OKASAKI, C., AND TARJAN, R. E. 1998. Simple confluently ... publication date: 9-Sep-2024. https://dl.acm.org/doi/10.1145/3678232.3678243.
  78. [78]
    CAS-Based Lock-Free Algorithm for Shared Deques - SpringerLink
    Michael, M.M., Scott, M.L.: Simple, fast, and practical non-blocking and blocking concurrent queue algorithms. In: Proceedings of the 15th Annual ACM ...
  79. [79]
    [PDF] Simple, Fast, and Practical Non-Blocking and Blocking Concurrent ...
    The two-lock concurrent queue outperforms a single lock when several processes are competing simultaneously for access; it ap- pears to be the algorithm of ...
  80. [80]
    [PDF] Even Better DCAS-Based Concurrent Deques - People | MIT CSAIL
    In a previous paper, we presented two linearizable non-blocking imple- mentations of concurrent deques (double-ended queues) using the DCAS operation. These ...