Hello! Welcome to the third lesson in our "Python Tools for Competitive Programming" module.
In our previous lessons, we've focused on writing concise and powerful code using list comprehensions and lambda functions. Today, we shift our focus from code style to a fundamental aspect of competitive programming: performance. You'll discover that Python's standard list isn't always the best tool for the job, especially when you need to add or remove items from the beginning of a sequence.
This lesson introduces collections.deque, a high-performance data structure from Python's standard library. You will learn why it's the ideal choice for implementing efficient queues (First-In, First-Out) and stacks (Last-In, First-Out), which are essential building blocks for countless algorithms you'll encounter.
1. The Problem: Why Lists Can Be Inefficient
Let's start by considering a queue. A queue is a collection that follows the "First-In, First-Out" (FIFO) principle. Think of a line of people waiting for a bus: the first person to join the line is the first person to get on the bus.
You can simulate a queue with a Python list:
- Enqueue (add to the end):
my_list.append(item) - Dequeue (remove from the front):
my_list.pop(0)
While append() is generally fast, pop(0) is very slow. When you remove the first element from a list, every other element in the list has to be shifted one position to the left. For a list with n elements, this is an O(n) operation. In competitive programming, where your code might process millions of items, this inefficiency can lead to a "Time Limit Exceeded" error.
The following video clip demonstrates this issue and sets the stage for a better solution.
Queue - Data Structures & Algorithms Tutorials In Python #8
This video from codebasics, titled "Queue - Data Structures & Algorithms Tutorials In Python #8," starts by explaining what a queue is and then shows why using a standard Python list for it is problematic.
Watch the segment from 05:30 to 07:42. Pay close attention to the demonstration of using list.insert(0, ...) and list.pop() and the explanation of the problems associated with this approach.
2. The Solution: collections.deque
To solve this performance problem, Python's collections module provides the deque (pronounced "deck") object, which stands for "double-ended queue".
A deque is specifically designed for fast additions and removals from both ends. It is implemented internally as a doubly linked list, which means that adding or removing an element from either the beginning or the end takes the same amount of time, regardless of the deque's size. This is a constant time, or O(1), operation.
# First, you need to import it
from collections import deque
The key methods for a deque are:
append(item): Adds an item to the right end. (O(1))pop(): Removes an item from the right end. (O(1))appendleft(item): Adds an item to the left end. (O(1))popleft(): Removes an item from the left end. (O(1))
This image provides a clear visual of how deque operations work.

The following short video brilliantly explains why a deque is so much faster for these operations compared to a list.
The Best Data Structure You’ve Never Heard of | Python Deques
The video "The Best Data Structure You’ve Never Heard of | Python Deques" by Daniel Boctor gives an excellent conceptual overview of the performance differences.
Watch from 01:37 to 02:41. This segment visualizes how a list needs to shift all its elements (O(n)) while a deque can simply adjust its internal pointers (O(1)) for left-side operations.
The performance difference is not minor. For operations at the start of a large collection, a deque can be thousands of times faster than a list.

3. Implementing a Queue with deque
With deque, implementing an efficient FIFO queue is straightforward and should be your default approach in Python.
- Enqueue: Add to the right with
append(). - Dequeue: Remove from the left with
popleft().
from collections import deque
# Initialize an empty queue
customers = deque()
# Customers arrive and are enqueued
customers.append("Alice")
customers.append("Bob")
customers.append("Charlie")
print("Current queue:", customers) # Output: deque(['Alice', 'Bob', 'Charlie'])
# Customers are served and dequeued
first_customer = customers.popleft()
print(f"Serving: {first_customer}") # Output: Serving: Alice
second_customer = customers.popleft()
print(f"Serving: {second_customer}") # Output: Serving: Bob
print("Remaining queue:", customers) # Output: deque(['Bob', 'Charlie'])
Both append and popleft are O(1) operations, making this implementation highly performant.
For a deeper dive, the following article provides a thorough guide and even shows how to wrap a deque in a custom class for a cleaner interface.
Python's deque: Implement Efficient Queues and Stacks
The article "Python's deque: Implement Efficient Queues and Stacks" from Real Python is an excellent resource. We'll focus on the section dedicated to building queues.
Read the section titled "Build Efficient Queues With deque". Pay attention to the restaurant queue example and the custom Queue class implementation, which is a great pattern to know.
4. Implementing a Stack with deque
A stack is a "Last-In, First-Out" (LIFO) data structure. Think of a stack of plates: you add a new plate to the top and you also remove a plate from the top.
A standard Python list can be used as an efficient stack:
- Push (add to top):
my_list.append(item) - Pop (remove from top):
my_list.pop()
Both of these are (amortized) O(1) operations. So, for stacks, a list is perfectly fine.
However, you can also use a deque to implement a stack. This can be useful for consistency if you are using deques for queues elsewhere in your code.
- Push:
my_deque.append(item) - Pop:
my_deque.pop()
from collections import deque
# Initialize an empty stack
history = deque()
# User visits pages (push)
history.append("google.com")
history.append("wikipedia.org")
history.append("realpython.com")
print("Browser history:", history) # Output: deque(['google.com', 'wikipedia.org', 'realpython.com'])
# User clicks the back button (pop)
last_page = history.pop()
print(f"Went back from: {last_page}") # Output: Went back from: realpython.com
print("Current history:", history) # Output: deque(['google.com', 'wikipedia.org'])
As with the list-based stack, these operations are O(1).
5. deque vs. list: A Quick Reference
For competitive programming, choosing the right data structure is critical. Here's a summary of the performance characteristics to help you decide.
Deque in Python: A Comprehensive Guide to Efficient Data ...
The article "Deque in Python" from AlgoCademy provides a concise table comparing the time complexities of deque and list.
Find the section titled "6. Deque vs. List: Performance Comparison" and study the table. This is the key takeaway of the lesson.
The rule of thumb: If you need to add or remove elements from the left side (the beginning) of a sequence, use a deque. For all other list-like purposes, a list is usually sufficient.
Test your understanding!
A palindrome is a word or phrase that reads the same forwards and backwards, like "racecar" or "level".
Using a deque, write a function is_palindrome(s: str) -> bool that checks if a string is a palindrome. Your function should work by adding all characters of the string to a deque and then repeatedly comparing and removing the characters from both ends.
Show answer
from collections import deque
def is_palindrome(s: str) -> bool:
# Create a deque from the string characters
char_deque = deque(s)
# While there are at least two characters left
while len(char_deque) > 1:
# Pop from both ends and compare
if char_deque.popleft() != char_deque.pop():
return False
# If the loop finishes, it's a palindrome
return True
# Test cases
print(is_palindrome("racecar")) # Output: True
print(is_palindrome("hello")) # Output: False
print(is_palindrome("level")) # Output: True
This solution is elegant because it directly uses the double-ended nature of the deque to solve the problem.
Conclusion
You've now learned about one of the most important performance tools in Python's standard library. Mastering deque is a significant step in writing code that is not only correct but also fast enough to pass the stringent time limits of competitive programming platforms.
Key Takeaways:
- Queues (FIFO): Always use a
dequefor efficient queues. Useappend()to enqueue andpopleft()to dequeue. - Stacks (LIFO): A standard
listworks well (appendandpop), but adequeis also a perfect fit (appendandpop). - Performance:
dequeprovides O(1) time complexity for adding and removing elements from both ends, while alistis O(n) for operations at its beginning. - When to use
deque: Whenever you anticipate frequent additions or removals from the front of a sequence.
Up Next: We will continue our exploration of the collections module with collections.Counter, a specialized dictionary subclass that is incredibly useful for counting hashable objects—a very common task in algorithmic problems.