Queue: FIFO and Deque Code Templates in 6 Languages
If you have not read the concept guide yet, start there for the intuition and complexity analysis. This page gives you the code you can memorize and adapt during an interview. Every template here keeps the hard parts in the comments, and the hard parts are the visited check in BFS, the size snapshot in level order, and the eviction rules in the monotonic deque.
Main Template: FIFO Queue for BFS
The single most common interview use of a queue is breadth-first search. The queue holds the frontier of nodes to visit, and the visited set is what keeps the frontier from exploding. This template produces the shortest number of steps to every reachable node, which is exactly what shortest-path style problems ask for.
Use this for Open the Lock and similar shortest-step problems.
graph TD
S["Enqueue start, mark visited"] --> Q{"Queue not empty?"}
Q -->|Yes| D["Dequeue node"]
D --> N["For each neighbor"]
N --> V{"Visited?"}
V -->|No| A["Mark visited, enqueue"]
A --> Q
V -->|Yes| Q
Q -->|No| E["Done"]
function bfs(graph, start) {
const queue = [start];
const visited = new Set([start]);
while (queue.length > 0) {
const node = queue.shift();
for (const neighbor of graph[node] || []) {
// Mark at enqueue time, not dequeue time. The first
// time we reach a node is already the shortest path,
// so later discoveries are pure duplicates.
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
return visited;
}The complexity of this shape is
Code Breakdown
Key Variables
queue: the frontier of nodes waiting to be processed. FIFO order means every node at distance d is dequeued before any node at distance d+1.visited: nodes already discovered. Without it, a cycle sends the loop into an infinite queue, and a diamond in the graph processes the same node multiple times.graph[node]: the neighbors of a node. In trees it becomesleftandright; in grids it becomes the four directions.
Visual Mechanism
The diagram above is the whole pattern: initialize, loop while the queue has anything, and mark visited when you enqueue. The rest of every BFS problem is deciding what “visited” means and what to return.
Critical Sections
The visited check belongs inside the neighbor loop, before the enqueue. If you mark a node only when it comes out of the queue, two neighbors that both discover the same third node will both add it, and the queue grows to the edge count instead of the node count. The result is the same visited set, but the runtime silently turns quadratic on dense graphs.
The result assembly depends on the problem. For reachability, visited alone computes it. For shortest path, you pair the visited set with a distance map and return the distance entry for the target.
Variations
1. Level-Order Traversal (Size Snapshot)
Read the queue size once per level. Process exactly that many nodes, then the queue holds exactly the next level. This is the classic Binary Tree Level Order Traversal shape and generalizes to “process by distance”.
Used in problems like Binary Tree Level Order Traversal and shortest-path problems that report counts per layer.
function levelOrder(root) {
const result = [];
if (!root) return result;
const queue = [root];
while (queue.length > 0) {
const levelSize = queue.length; // snapshot BEFORE processing
const currentLevel = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
currentLevel.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(currentLevel);
}
return result;
}The snapshot is the whole trick. Processing with a live length check mixes the next level into the current one. Take the size first, loop exactly that many times, then the outer loop naturally starts the next level.
The complexity is
2. Monotonic Deque (Sliding Window Maximum)
Use this when the window slides and you need the maximum element of every window in
graph LR
A["New index i"] --> B{"Front < i - k + 1?"}
B -->|Yes| C["Pop front (out of window)"]
C --> D{"Back value <= nums[i]?"}
D -->|Yes| E["Pop back"]
D -->|No| F["Push i"]
E --> D
F --> G{"i >= k-1?"}
G -->|Yes| H["Output front value"]
Use this for Sliding Window Maximum .
var maxSlidingWindow = function(nums, k) {
const result = [];
const deque = []; // stores indices, in decreasing value order
for (let i = 0; i < nums.length; i++) {
// Evict indices that have slid out of the window.
while (deque.length > 0 && deque[0] < i - k + 1) {
deque.shift();
}
// Evict indices whose value loses to the new one. The
// new element is both larger and lives longer in the
// window, so the old one can never be the answer.
while (deque.length > 0 && nums[deque[deque.length - 1]] <= nums[i]) {
deque.pop();
}
deque.push(i);
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}
return result;
};Each index is pushed and popped at most once, so the total cost is
Next Steps
Both templates are now memorized, or close to it. The quickest way to lock in is application, so head to the practice problems page and work with these templates on real inputs. That page also links to related patterns to expand further.