Table of Contents

    deque

    PYTHON & DATA STRUCTURES

    Python deque — The Complete Guide

    Master collections.deque — the double-ended queue for fast appends and pops from both ends, queues, stacks, and sliding windows.

    Introduction

    deque (pronounced "deck", short for double-ended queue) is a list-like container from Python's built-in collections module, optimized for fast appends and pops from both ends. While a normal list is slow when you insert or remove at the front (an \(O(n)\) operation), a deque does it in \(O(1)\). This makes it the go-to structure for queues, stacks, breadth-first search, and sliding-window algorithms.

    In one line: A deque is a list optimized for lightning-fast adds and removes at both ends.

    Real-World Analogy

    The Double-Door Train Car

    Picture a train carriage with doors at both the front and the back. Passengers can board or exit from either end instantly, with no shuffling down the aisle. A deque is that carriage — you add or remove from the left or right end equally fast.

    Prerequisites

    Before You Start

    • Python 3.x installed
    • Understanding of lists and basic operations (append, pop, indexing)
    • The module is built in — just from collections import deque
    • Familiarity with queues (FIFO) and stacks (LIFO) concepts
    • Awareness of Big-O time complexity (helpful)

    Getting Started

    Create a deque from any iterable, then add and remove from either end.

    from collections import deque
    
    d = deque([1, 2, 3])
    
    d.append(4)        # add to the right   -> deque([1, 2, 3, 4])
    d.appendleft(0)    # add to the left    -> deque([0, 1, 2, 3, 4])
    
    d.pop()            # remove from right  -> returns 4
    d.popleft()        # remove from left   -> returns 0
    
    print(d)           # deque([1, 2, 3])
    Note The *left methods (appendleft, popleft) are what make a deque special — a list has no fast equivalent.

    Essential deque Methods

    Method Purpose
    append(x) Add x to the right end
    appendleft(x) Add x to the left end
    pop() Remove and return the rightmost item
    popleft() Remove and return the leftmost item
    extend(iterable) Add multiple items to the right
    extendleft(iterable) Add multiple items to the left (reversed order)
    rotate(n) Rotate the deque n steps to the right
    maxlen Optional fixed maximum length (read-only attribute)

    Rotating a deque

    rotate(n) shifts elements to the right; a negative n shifts left.

    from collections import deque
    
    d = deque([1, 2, 3, 4, 5])
    
    d.rotate(2)     # right by 2
    print(d)        # deque([4, 5, 1, 2, 3])
    
    d.rotate(-1)    # left by 1
    print(d)        # deque([5, 1, 2, 3, 4])

    Bounded deque with maxlen

    A deque with maxlen automatically discards items from the opposite end when full — perfect for sliding windows and rolling logs.

    from collections import deque
    
    # Keep only the last 3 items
    recent = deque(maxlen=3)
    
    for i in range(6):
        recent.append(i)
        print(recent)
    
    # deque([0], maxlen=3)
    # deque([0, 1], maxlen=3)
    # deque([0, 1, 2], maxlen=3)
    # deque([1, 2, 3], maxlen=3)   <- 0 dropped
    # deque([2, 3, 4], maxlen=3)
    # deque([3, 4, 5], maxlen=3)
    Use case A bounded deque is ideal for "last N events", moving averages, and undo history with a size cap.

    Using deque as a Queue (FIFO)

    Add to the right, remove from the left — first in, first out.

    from collections import deque
    
    queue = deque()
    queue.append("task1")     # enqueue
    queue.append("task2")
    queue.append("task3")
    
    print(queue.popleft())    # task1  (dequeue)
    print(queue.popleft())    # task2
    print(queue)              # deque(['task3'])

    Using deque as a Stack (LIFO)

    Add and remove from the same (right) end — last in, first out.

    from collections import deque
    
    stack = deque()
    stack.append("page1")     # push
    stack.append("page2")
    stack.append("page3")
    
    print(stack.pop())        # page3  (pop)
    print(stack.pop())        # page2
    print(stack)              # deque(['page1'])

    Real Example: Breadth-First Search

    BFS is the classic deque application — a FIFO frontier of nodes to visit.

    from collections import deque
    
    graph = {
        "A": ["B", "C"],
        "B": ["D", "E"],
        "C": ["F"],
        "D": [], "E": [], "F": [],
    }
    
    def bfs(start):
        visited = []
        queue = deque([start])
        while queue:
            node = queue.popleft()      # FIFO
            if node not in visited:
                visited.append(node)
                queue.extend(graph[node])
        return visited
    
    print(bfs("A"))   # ['A', 'B', 'C', 'D', 'E', 'F']

    deque vs list Performance

    Operation deque list
    Append right O(1) O(1)
    Pop right O(1) O(1)
    Append left O(1) O(n)
    Pop left O(1) O(n)
    Random index access O(n) O(1)
    Trade-off A deque is slow at random indexing (d[i] in the middle is O(n)). If you need fast random access, use a list.

    Why Front Operations Are Fast

    A list stores items in a contiguous array, so inserting at the front shifts all \(n\) elements — cost \(O(n)\). A deque is a doubly-linked structure of blocks, so end operations cost a constant:

    \[ T_{\text{list front}} = O(n) \qquad\qquad T_{\text{deque front}} = O(1) \]

    For workloads dominated by front insertions/removals, a deque is dramatically faster.

    Best Practices

    Do This

    • Use a deque for queues (FIFO) instead of a list with pop(0)
    • Use maxlen for fixed-size sliding windows and rolling buffers
    • Use appendleft/popleft when you need fast front access
    • Use a deque for BFS and level-order traversals
    • Prefer a list when you need frequent random index access
    • Remember extendleft inserts items in reversed order

    Common Mistakes

    Bad Using a list as a queue with list.pop(0) — it's O(n) and slow for large data.
    Good Use deque.popleft() — an O(1) front removal.
    Bad Expecting extendleft([1, 2, 3]) to keep order — it inserts them reversed as 3, 2, 1.
    Good Reverse first if order matters, or append them one at a time.

    Interview Questions

    Question Short Answer
    What is a deque? A double-ended queue supporting O(1) appends/pops at both ends.
    Why use deque over list for a queue? List's pop(0) is O(n); deque's popleft() is O(1).
    What does maxlen do? Caps the size; adding past it drops items from the opposite end.
    Weakness of a deque? Random middle indexing is O(n), unlike a list's O(1).
    How does extendleft behave? It adds items to the left in reversed order.

    Quick Revision

    Goal Code
    Create deque(iterable)
    Add both ends append() / appendleft()
    Remove both ends pop() / popleft()
    Fixed window deque(maxlen=N)
    Rotate rotate(n)

    Key Takeaways

    deque gives you O(1) operations at both ends, making it ideal for queues, stacks, BFS, and sliding windows (with maxlen). Choose it over a list for front operations — but stick to a list when you need fast random indexing.