Skip to content

Commit 7563da8

Browse files
committed
Deque: use DoublyLinkedList so both ends are truly O(1)
1 parent 6bb32ed commit 7563da8

3 files changed

Lines changed: 95 additions & 87 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Remember that each data has its own trade-offs. And you need to pay attention mo
5555
* `B` [Doubly Linked List](src/data-structures/doubly-linked-list)
5656
* `B` [Queue](src/data-structures/queue)
5757
* `B` [Stack](src/data-structures/stack)
58+
* `B` [Deque](src/data-structures/deque) - double-ended queue
5859
* `B` [Hash Table](src/data-structures/hash-table)
5960
* `B` [Heap](src/data-structures/heap) - max and min heap versions
6061
* `B` [Priority Queue](src/data-structures/priority-queue)

src/data-structures/deque/Deque.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import LinkedList from '../linked-list/LinkedList';
1+
import DoublyLinkedList from '../doubly-linked-list/DoublyLinkedList';
22

33
export default class Deque {
44
constructor() {
55
// We use a doubly linked list internally so that both front and back
6-
// operations run in O(1) time.
7-
this.linkedList = new LinkedList();
6+
// operations (add, remove and peek) run in O(1) time.
7+
this.linkedList = new DoublyLinkedList();
8+
9+
// Keep a running element count so that `size` stays O(1).
10+
this.length = 0;
811
}
912

1013
/**
@@ -43,6 +46,7 @@ export default class Deque {
4346
*/
4447
addFront(value) {
4548
this.linkedList.prepend(value);
49+
this.length += 1;
4650
}
4751

4852
/**
@@ -51,6 +55,7 @@ export default class Deque {
5155
*/
5256
addBack(value) {
5357
this.linkedList.append(value);
58+
this.length += 1;
5459
}
5560

5661
/**
@@ -59,7 +64,11 @@ export default class Deque {
5964
*/
6065
removeFront() {
6166
const removedHead = this.linkedList.deleteHead();
62-
return removedHead ? removedHead.value : null;
67+
if (!removedHead) {
68+
return null;
69+
}
70+
this.length -= 1;
71+
return removedHead.value;
6372
}
6473

6574
/**
@@ -68,21 +77,19 @@ export default class Deque {
6877
*/
6978
removeBack() {
7079
const removedTail = this.linkedList.deleteTail();
71-
return removedTail ? removedTail.value : null;
80+
if (!removedTail) {
81+
return null;
82+
}
83+
this.length -= 1;
84+
return removedTail.value;
7285
}
7386

7487
/**
7588
* Return the number of elements in the deque.
7689
* @return {number}
7790
*/
7891
get size() {
79-
let count = 0;
80-
let currentNode = this.linkedList.head;
81-
while (currentNode) {
82-
count += 1;
83-
currentNode = currentNode.next;
84-
}
85-
return count;
92+
return this.length;
8693
}
8794

8895
/**
Lines changed: 75 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,75 @@
1-
# Deque (Double-Ended Queue)
2-
3-
A **deque** (pronounced "deck", short for **double-ended queue**) is a linear data
4-
structure that generalises both a stack and a queue. Elements can be added or
5-
removed from **either end** — the front (head) or the back (tail) — in **O(1)** time.
6-
7-
```
8-
addFront(3) addBack(4)
9-
10-
┌───┬───┬──────┐
11-
│ 3 │ 1 │ 2 │ 4 │ ← internal linked list
12-
└───┴───┴──────┘
13-
14-
removeFront() removeBack()
15-
```
16-
17-
## Operations
18-
19-
| Method | Description | Time |
20-
| ------------- | -------------------------------------------- | ---- |
21-
| `addFront(v)` | Insert element `v` at the front | O(1) |
22-
| `addBack(v)` | Insert element `v` at the back | O(1) |
23-
| `removeFront()` | Remove and return the front element | O(1) |
24-
| `removeBack()` | Remove and return the back element | O(1) |
25-
| `peekFront()` | Return the front element without removing it | O(1) |
26-
| `peekBack()` | Return the back element without removing it | O(1) |
27-
| `isEmpty()` | Return `true` if the deque has no elements | O(1) |
28-
| `size` | Return the number of elements | O(n) |
29-
30-
> **Note:** `size` is O(n) because the underlying linked list does not cache
31-
> length. If you call `size` frequently, consider maintaining an internal counter.
32-
33-
## Complexity
34-
35-
| | |
36-
| --------- | ---- |
37-
| Space | O(n) |
38-
| addFront | O(1) |
39-
| addBack | O(1) |
40-
| removeFront | O(1) |
41-
| removeBack | O(1) |
42-
| peekFront | O(1) |
43-
| peekBack | O(1) |
44-
45-
## Use Cases
46-
47-
A deque is the right tool when you need **O(1) access at both ends**:
48-
49-
- **Sliding window maximum/minimum**maintain candidates in a monotonic deque
50-
so each element is pushed and popped at most once (overall O(n)).
51-
- **Browser history**navigate backward (`removeFront`) and forward
52-
(`removeBack`) through pages.
53-
- **Undo / redo stacks**push actions to the back, undo from the back,
54-
redo from the front.
55-
- **Palindrome checking** — compare characters from both ends simultaneously.
56-
- **Work-stealing schedulers** (e.g. Java's `ForkJoinPool`) — threads push/pop
57-
from their own back, while idle threads steal from another thread's front.
58-
59-
## Implementation Note
60-
61-
This implementation is backed by the project's existing `LinkedList` (a doubly
62-
linked list). This gives O(1) `prepend` (for `addFront`) and O(1) `append` /
63-
`deleteTail` (for `addBack` / `removeBack`), with no need to shift array
64-
elements.
65-
66-
An alternative implementation using a circular buffer (fixed-size array) offers
67-
better cache locality but requires resizing logic. The linked-list approach is
68-
chosen here to stay consistent with the other data structures in this project.
69-
70-
## References
71-
72-
- [Deque — Wikipedia](https://en.wikipedia.org/wiki/Double-ended_queue)
73-
- [Deque Data Structure — GeeksForGeeks](https://www.geeksforgeeks.org/deque-set-1-introduction-applications/)
74-
- [▶ Deque in 3 minutes — YouTube](https://www.youtube.com/watch?v=kLBuJ1998Do)
75-
- [Sliding Window Maximum using Deque — YouTube](https://www.youtube.com/watch?v=2SXqBsTR6a8)
1+
# Deque (Double-Ended Queue)
2+
3+
A **deque** (pronounced "deck", short for **double-ended queue**) is a linear data
4+
structure that generalizes both a stack and a queue. Elements can be added or
5+
removed from **either end** — the front (head) or the back (tail) — in **O(1)** time.
6+
7+
```
8+
addFront(3) addBack(4)
9+
10+
┌───────┬───────┬───────┬───────┐
11+
│ 3 │ 1 │ 2 │ 4 │ ← internal doubly linked list
12+
└───────┴───────┴───────┴───────┘
13+
14+
removeFront() removeBack()
15+
```
16+
17+
## Operations
18+
19+
| Method | Description | Time |
20+
| --------------- | -------------------------------------------- | ---- |
21+
| `addFront(v)` | Insert element `v` at the front | O(1) |
22+
| `addBack(v)` | Insert element `v` at the back | O(1) |
23+
| `removeFront()` | Remove and return the front element | O(1) |
24+
| `removeBack()` | Remove and return the back element | O(1) |
25+
| `peekFront()` | Return the front element without removing it | O(1) |
26+
| `peekBack()` | Return the back element without removing it | O(1) |
27+
| `isEmpty()` | Return `true` if the deque has no elements | O(1) |
28+
| `size` | Return the number of elements | O(1) |
29+
30+
> **Note:** `size` is O(1) because the deque keeps a running element count that
31+
> is updated on every add and remove operation.
32+
33+
## Complexity
34+
35+
| Operation | Time |
36+
| ---------------------------- | ---- |
37+
| `addFront` / `addBack` | O(1) |
38+
| `removeFront` / `removeBack` | O(1) |
39+
| `peekFront` / `peekBack` | O(1) |
40+
| `isEmpty` / `size` | O(1) |
41+
| Space | O(n) |
42+
43+
## Use Cases
44+
45+
A deque is the right tool when you need **O(1) access at both ends**:
46+
47+
- **Sliding window maximum/minimum** — maintain candidates in a monotonic deque
48+
so each element is pushed and popped at most once (overall O(n)).
49+
- **Browser history**navigate backward (`removeFront`) and forward
50+
(`removeBack`) through pages.
51+
- **Undo / redo stacks**push actions to the back, undo from the back,
52+
redo from the front.
53+
- **Palindrome checking**compare characters from both ends simultaneously.
54+
- **Work-stealing schedulers** (e.g. Java's `ForkJoinPool`) — threads push/pop
55+
from their own back, while idle threads steal from another thread's front.
56+
57+
## Implementation Note
58+
59+
This implementation is backed by the project's existing
60+
[`DoublyLinkedList`](../doubly-linked-list). Because every node keeps a reference
61+
to both its previous and next neighbours, this gives O(1) `prepend` (for
62+
`addFront`), O(1) `deleteHead` (for `removeFront`) and O(1) `append` /
63+
`deleteTail` (for `addBack` / `removeBack`), with no need to shift array
64+
elements. The number of elements is tracked in a counter, so `size` is O(1) too.
65+
66+
An alternative implementation using a circular buffer (fixed-size array) offers
67+
better cache locality but requires resizing logic. The linked-list approach is
68+
chosen here to stay consistent with the other data structures in this project.
69+
70+
## References
71+
72+
- [Deque — Wikipedia](https://en.wikipedia.org/wiki/Double-ended_queue)
73+
- [Deque Data Structure — GeeksForGeeks](https://www.geeksforgeeks.org/deque-set-1-introduction-applications/)
74+
- [▶ Deque (Double-Ended Queue) — YouTube](https://www.youtube.com/watch?v=pqg0SOPRlJ4)
75+
- [Sliding Window Maximum using Deque — YouTube](https://www.youtube.com/watch?v=2SXqBsTR6a8)

0 commit comments

Comments
 (0)