Why Deque Beats Vector and List: Inside the Double‑Ended Queue’s Magic
This article explains how the C++ deque combines the random‑access speed of a vector with the constant‑time double‑ended insertions of a list by using a segmented storage architecture, detailing its internal map, iterator mechanics, core operations, performance comparisons, practical use cases, and common pitfalls.
What is a deque?
In many development scenarios you need to frequently add or remove data at both ends, such as implementing sliding‑window algorithms, double‑ended queue components, or the underlying containers for stacks and queues. Vector offers fast tail insertion but costly head insertion, while list provides fast double‑ended operations but poor random access. Deque fills this gap by supporting random access like a vector and O(1) double‑ended insertion/deletion like a list.
Deque internal implementation
2.1 Segmented storage architecture
Deque does not use a single contiguous memory block like vector nor a linked list like list. Instead it consists of a control map (an array of pointers) and fixed‑size buffers (segments). Each buffer holds a chunk of elements (e.g., 512 bytes in SGI STL). When a buffer fills, a new buffer is allocated and linked via the control map, avoiding large reallocations and reducing fragmentation.
2.2 Control map: directory system
The control map is essentially a pointer directory where each entry points to a buffer. When the map needs to grow, a new map is allocated and existing pointers are copied, which is far cheaper than moving all elements as vector does during reallocation.
2.3 Iterator cross‑boundary magic
Because deque’s storage is segmented, its iterator cannot be a simple pointer. It stores four members: cur (current element), first (buffer start), last (buffer end), and map_ptr (pointer to the map entry). When the iterator reaches a buffer boundary, it uses map_ptr to jump to the next or previous buffer, making traversal appear logically continuous.
Case 1: ++it (forward cross‑boundary) Detect that cur + 1 == last (end of current buffer). Advance map_ptr to the next buffer. Set first and last to the new buffer’s bounds. Set cur to first , completing the move.
Case 2: --it (backward cross‑boundary) Detect that cur == first (beginning of current buffer). Move map_ptr to the previous buffer. Update first and last to the previous buffer’s bounds. Set cur to last - 1 , the last element of that buffer.
Core operations: double‑ended efficiency
3.1 Double‑ended insertion / deletion (O(1))
When push_back is called, deque checks whether the tail buffer has free space. If so, the element is placed directly; otherwise a new buffer is allocated, the map is updated, and the element is inserted at the start of the new buffer. The amortized cost remains O(1). push_front works symmetrically.
<code class="language-cpp">class Deque:
def __init__(self, buffer_size=4):
self.buffer_size = buffer_size
self.map = []
self.head_buffer = 0
self.head_pos = 0
self.tail_buffer = 0
self.tail_pos = -1
self.size = 0
def push_back(self, value):
if self.size == 0:
self.map.append([None] * self.buffer_size)
self.tail_pos = 0
self.map[self.tail_buffer][self.tail_pos] = value
self.size += 1
return
if self.tail_pos == self.buffer_size - 1:
self.map.append([None] * self.buffer_size)
self.tail_buffer += 1
self.tail_pos = 0
else:
self.tail_pos += 1
self.map[self.tail_buffer][self.tail_pos] = value
self.size += 1
</code>3.2 Random access (O(1))
To access the n‑th element, deque computes the buffer index buf_idx = n / buffer_size and the offset offset = n % buffer_size, then retrieves the element via the control map. This adds one extra pointer indirection compared with vector but still runs in constant time.
<code class="language-cpp">def __getitem__(self, index):
if index < 0 or index >= self.size:
raise IndexError("deque index out of range")
buf_idx = index // self.buffer_size
offset = index % self.buffer_size
return self.map[buf_idx][offset]
</code>3.3 Middle operations are costly
Insertion or deletion in the middle requires shifting elements within a buffer and possibly across buffers, leading to O(n) complexity, similar to vector and far slower than list for such operations.
3.4 Capacity management
Deque does not expose a capacity() function. Its segmented design allows it to grow by adding new buffers without a large contiguous allocation, which saves memory but prevents pre‑allocation tricks like vector::reserve() for bulk inserts.
Comparison with sibling containers
Vector provides contiguous storage and excellent random access, but head insertions are O(n). List offers O(1) insertion/deletion anywhere via pointers, but random access is O(n). Deque combines random access (O(1)) with O(1) double‑ended insert/delete, making it ideal for queue‑like workloads that also need occasional indexing.
Practical use cases
Implementing a double‑ended queue for task scheduling where high‑priority tasks are pushed to the front and normal tasks to the back.
Sliding‑window algorithms that need fast removal from the front and fast addition to the back while still allowing random access to window elements.
Log buffers that keep the most recent N entries, automatically discarding the oldest when new entries arrive.
Common pitfalls
All iterators become invalid after any insertion or deletion, unlike vector where only reallocation invalidates them.
Because storage is non‑contiguous, &deque[0] cannot be used with C‑style functions that require a continuous memory block (e.g., memcpy).
Debuggers may only show the first buffer; to inspect the whole deque, iterate it or copy it into a std::vector for easier viewing.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
