Pattern #11
Queue & Deque
RecommendedFIFO processing, sliding-window extrema, and recent-event tracking.
When to use
Order of processing matters (BFS-like), or you need min/max in a moving window in amortized O(1).
Recognition cues
- Sliding window maximum
- Moving average / hit counter
- Design circular queue / recent calls
Common pitfalls
- Forgetting to evict indices that left the window
- Using a list pop(0) in Python (O(n)) instead of deque
- Off-by-one on inclusive window bounds
90-second recognition drill
Which pattern fits best?
- Sliding window maximum
- Moving average / hit counter
- Design circular queue / recent calls
Interactive
Mental model
A full worked walkthrough of the invariant. Pause, scrub the dots, or use ← →. Aim to narrate each step yourself.
front = current max index
Sliding window maximum with k = 3. Deque holds candidates decreasing.
How to think about it
A queue preserves arrival order. A monotonic deque stores candidate indices for window min/max: front is always the answer; back drops dominated values. Combined with a sliding window, each index enters and leaves once.
Template shapes
| Shape | Core move | Notes |
|---|---|---|
| FIFO queue | Enqueue / dequeue | BFS, buffering |
| Monotonic deque | Pop back if worse | Window max/min |
| Time window | Drop expired front | Hit counter |
Complexity baseline
O(n) amortized for window extrema; O(1) per op for simple queue designs.
From template to problem
- Choose queue vs deque based on whether you need both ends.
- For window max: maintain decreasing values in the deque.
- Evict front when out of window; push new index after cleaning the back.
- Record front after the window is full.
Template
Same skeleton in TypeScript, Python, and C#. Adapt the invariant; keep the structure.
/** Deque template: sliding window maximum. */
export function maxSlidingWindow(nums: number[], k: number): number[] {
const dq: number[] = [];
const res: number[] = [];
for (let i = 0; i < nums.length; i++) {
while (dq.length && dq[0]! <= i - k) dq.shift();
while (dq.length && nums[dq[dq.length - 1]!]! <= nums[i]!) dq.pop();
dq.push(i);
if (i >= k - 1) res.push(nums[dq[0]!]!);
}
return res;
}
/** Deque template: sliding window maximum. */
export function maxSlidingWindow(nums: number[], k: number): number[] {
const dq: number[] = [];
const res: number[] = [];
for (let i = 0; i < nums.length; i++) {
while (dq.length && dq[0]! <= i - k) dq.shift();
while (dq.length && nums[dq[dq.length - 1]!]! <= nums[i]!) dq.pop();
dq.push(i);
if (i >= k - 1) res.push(nums[dq[0]!]!);
}
return res;
}
- 1#239 Sliding Window MaximumGuidehard
- 2#346 Moving Average from Data StreamGuideeasy
- 3#362 Design Hit CounterGuidemedium
- 4#622 Design Circular QueueGuidemedium
- 5#641 Design Circular DequeGuidemedium
- 6#933 Number of Recent CallsGuideeasy