Deques and Priority Queues
Deque ADT
Section titled “Deque ADT”A deque (double-ended queue) is a linear collection that supports insertion and removal at both Ends. It generalises both stacks (LIFO) and queues (FIFO).
Operations
Section titled “Operations”| Operation | Description | Array-backed | Linked-list |
|---|---|---|---|
push_front(x) | Insert at front | ||
push_back(x) | Insert at back | ||
pop_front() | Remove from front | ||
pop_back() | Remove from back | ||
front() | Access front element | ||
back() | Access back element | ||
is_empty() | Check if empty | ||
size() | Number of elements |
Circular Buffer Implementation
Section titled “Circular Buffer Implementation”A circular buffer (ring buffer) implements a deque using a fixed-size array with two indices (head and tail) that wrap around.
class CircularBufferDeque: """ Deque using a circular buffer (dynamic array). Time: O(1) amortised for all operations Space: O(n) """ def __init__(self, capacity=16): self.capacity = capacity self.data = [None] * capacity self.head = 0 self.tail = 0 self.size = 0
def push_front(self, value): if self.size == self.capacity: self._resize() self.head = (self.head - 1) % self.capacity self.data[self.head] = value self.size += 1
def push_back(self, value): if self.size == self.capacity: self._resize() self.data[self.tail] = value self.tail = (self.tail + 1) % self.capacity self.size += 1
def pop_front(self): if self.size == 0: raise IndexError("pop from empty deque") value = self.data[self.head] self.head = (self.head + 1) % self.capacity self.size -= 1 return value
def pop_back(self): if self.size == 0: raise IndexError("pop from empty deque") self.tail = (self.tail - 1) % self.capacity value = self.data[self.tail] self.size -= 1 return value
def front(self): if self.size == 0: raise IndexError("front of empty deque") return self.data[self.head]
def back(self): if self.size == 0: raise IndexError("back of empty deque") return self.data[(self.tail - 1) % self.capacity]
def _resize(self): new_data = [None] * (self.capacity * 2) for i in range(self.size): new_data[i] = self.data[(self.head + i) % self.capacity] self.data = new_data self.head = 0 self.tail = self.size self.capacity *= 2graph LR
subgraph Circular Buffer
direction LR
A[5] --> B[12] --> C[7] --> D[3] --> E[.] --> F[.] --> G[.] --> H[.]
end
HEAD["head=2"] -.-> C
TAIL["tail=5"] -.-> EDoubly-Linked List Implementation
Section titled “Doubly-Linked List Implementation”class DequeNode: __slots__ = ("val', 'prev', 'next') def __init__(self, val): self.val = val self.prev = None self.next = None
class LinkedListDeque: """ Deque using a doubly-linked list with sentinel nodes. Time: O(1) for all operations Space: O(n), one node per element plus two sentinels """ def __init__(self): self.sentinel = DequeNode(None) self.sentinel.prev = self.sentinel self.sentinel.next = self.sentinel self.size = 0
def push_front(self, value): node = DequeNode(value) node.next = self.sentinel.next node.prev = self.sentinel self.sentinel.next.prev = node self.sentinel.next = node self.size += 1
def push_back(self, value): node = DequeNode(value) node.prev = self.sentinel.prev node.next = self.sentinel self.sentinel.prev.next = node self.sentinel.prev = node self.size += 1
def pop_front(self): if self.size == 0: raise IndexError("pop from empty deque") node = self.sentinel.next node.next.prev = self.sentinel self.sentinel.next = node.next self.size -= 1 return node.val
def pop_back(self): if self.size == 0: raise IndexError("pop from empty deque") node = self.sentinel.prev node.prev.next = self.sentinel self.sentinel.prev = node.prev self.size -= 1 return node.valStandard Library Support
Section titled “Standard Library Support”| Language | Type | Implementation | Notes |
|---|---|---|---|
| Python | collections.deque | Circular buffer | all operations |
| C++ | std::deque | Segmented array | all operations |
| Java | ArrayDeque | Circular buffer | all operations |
| Rust | VecDeque | Ring buffer | all operations |
| Go | None (use slice) | N/A | Manual implementation needed |