CARVIEW |
- Log in to:
- Community
- DigitalOcean
- Sign up for:
- Community
- DigitalOcean
Sr Technical Writer

Introduction
A priority queue is a data structure that stores elements with assigned priorities and allows you to efficiently retrieve the element with the highest (or lowest) priority. In Python, you have several options for implementing priority queues:
- The
heapq
module provides a fast and memory-efficient implementation of a min-heap priority queue - For thread-safe applications, the
queue.PriorityQueue
class offers a synchronized wrapper aroundheapq
- You can create a max-heap by either:
- Inverting priorities (using negative values)
- Implementing a custom class with the
__lt__
comparison method
This tutorial will show you how to use each approach with practical examples.
Key Takeaways
After reading this tutorial, you will be able to:
- Understand what a priority queue is and how it differs from regular queues
- Implement a basic priority queue using Python’s built-in
heapq
module - Create thread-safe priority queues with the
queue.PriorityQueue
class - Build both min-heap and max-heap priority queues through priority inversion
- Develop custom priority queue classes by implementing comparison methods
- Choose the right priority queue implementation for your specific use case
- Apply priority queues to real-world scenarios like process scheduling, task management, and resource allocation
- Optimize your applications by leveraging priority queues for ordered data processing
- Debug common issues when working with priority queues in Python
- Follow best practices for priority queue implementation and usage
Key Concepts
PriorityQueue
is a thread-safe implementation of a priority queue, ensuring safe access and modification in multi-threaded environments.- The
put
method is used to add tasks to the priority queue, where the first argument is the priority and the second argument is the task itself. - The
get
method retrieves the task with the highest priority from the queue. - The
task_done
method is used to indicate that a task has been completed. - The
join
method blocks until all tasks in the queue have been processed and completed.
Prerequisites
Before you start, make sure you have the following prerequisites:
- Python 3.7 or newer installed
- Familiarity with lists, functions, and classes.
- Optional: basic multithreading knowledge
What Is a Priority Queue?
A priority queue stores (priority, item) pairs so the element with the highest priority (or lowest, for min-heap) is removed first. Python ships two ready-made solutions: heapq
and queue.PriorityQueue
.
Priority queues are incredibly useful in various real-world applications and can benefit different types of users:
What are some use cases and applications for priority queues?
- Operating Systems: Process scheduling where high-priority tasks need to be executed first
- Network Routers: Managing network traffic by prioritizing certain types of data packets
- Healthcare Systems: Organizing emergency room patients based on condition severity
- Task Management Software: Handling tasks based on urgency and importance
- Game Development: AI decision making and event scheduling
- Resource Management: Allocating limited resources to competing requests
Who Can Use Priority Queues?
-
Software Developers
- Backend developers implementing job queues
- Game developers managing game events
- System programmers working on schedulers
-
Data Scientists
- Implementing algorithms like Dijkstra’s shortest path
- Managing computational tasks in order of importance
-
System Architects
- Designing distributed systems
- Building load balancers and request handlers
-
Business Applications
- Customer service ticket systems
- Project management tools
- Inventory management systems
Priority queues are particularly valuable when you need to:
- Process items in a specific order based on importance
- Manage limited resources efficiently
- Handle real-time events that require immediate attention
- Implement algorithms that require ordered processing
How to Implement a Priority Queue Using heapq
?
heapq
?The heapq
module provides a min-heap implementation that can be used to implement a priority queue.
This code block demonstrates the usage of a priority queue implemented using the heapq
module in Python. A priority queue is a data structure that stores elements with associated priorities, allowing for efficient retrieval of the element with the highest or lowest priority.
The code initializes an empty priority queue and pushes three tasks with different priorities into the queue. The tasks are represented as tuples, where the first element is the priority and the second element is the task description.
The heapq.heappush
function is used to add tasks to the queue, and the heapq.heappop
function is used to remove and return the task with the smallest priority.
import heapq
pq = []
# push
heapq.heappush(pq, (2, "code"))
heapq.heappush(pq, (1, "eat"))
heapq.heappush(pq, (3, "sleep"))
# pop – always smallest priority
priority, task = heapq.heappop(pq)
print(priority, task) # 1 eat
Output1 eat
2 code
3 sleep
The output of the code shows that the task with the smallest priority (“eat” with priority 1) is retrieved first, followed by the tasks with higher priorities (“code” with priority 2 and “sleep” with priority 3).
heapq
maintains the smallest tuple at index 0, ensuring efficient retrieval of the highest priority element. Each push and pop operation incurs a time complexity of O(log n), where n
is the number of elements in the heap. The space complexity is O(n), as the heap stores all elements.
Benefits of Using heapq
Benefit | Description |
---|---|
Efficiency | heapq maintains the smallest tuple at index 0, ensuring efficient retrieval of the highest priority element. |
Simplicity | heapq is a built-in module that requires no additional setup. |
Performance | heapq is optimized for speed and memory usage. |
Limitations of Using heapq
Limitation | Description |
---|---|
No Maximum Priority | heapq by default only supports min-heap, so you cannot use it to implement a max-heap. |
No Priority Update | heapq does not support updating the priority of an existing element. |
What is a Min-Heap vs Max-Heap?
A min-heap and max-heap are tree-based data structures that satisfy specific ordering properties:
Min-Heap
- The value of each node is less than or equal to the values of its children
- The root node contains the minimum value in the heap
- Used when you need to efficiently find/remove the smallest element
- Python’s
heapq
implements a min-heap
Example min-heap:
1
/ \
3 2
/ \ /
6 4 5
You can read more about it in this tutorial on min-heap-binary-tree.
Max-Heap
- The value of each node is greater than or equal to the values of its children
- The root node contains the maximum value in the heap
- Used when you need to efficiently find/remove the largest element
Example max-heap:
6
/ \
4 5
/ \ /
1 3 2
How to Implement a Max-Heap using heapq
?
heapq
?So by default heapq
only supports min-heap, but you can implement a max-heap by either:
- Inverting priorities (using negative values)
- Implementing a custom class with the
__lt__
comparison method
Let’s find out how to implement a max-heap using heapq
with both approaches.
1. How to Implement a Max-Heap using Inverting Priorities(using negative values)
A max-heap can be simulated using heapq
by negating the values before adding them to the heap and then negating them again when extracting the maximum value. This works because negating numbers reverses their natural order (e.g., if a > b
, then -a < -b
), allowing the min-heap to effectively store and retrieve values in a max-heap manner.
import heapq
# Initialize an empty list to act as the heap
max_heap = []
# Push elements into the simulated max-heap by negating them
heapq.heappush(max_heap, -5)
heapq.heappush(max_heap, -1)
heapq.heappush(max_heap, -8)
# Pop the largest element (which was stored as the smallest negative value)
largest_element = -heapq.heappop(max_heap)
print(f"Largest element: {largest_element}")
OutputLargest element: 8
The output shows that the largest element (8) is retrieved first, followed by the elements with lower values (-5 and -1).
Space complexity: O(n)
, where n is the number of elements in the heap. This is because we store all elements in the heap.
Time complexity: O(log n)
for each insertion and extraction operation. This is because heapq.heappush
and heapq.heappop
operations take O(log n)
time.
Note: The time complexity for the entire process is O(n log n)
due to the n insertions and one extraction operation.
Benefits of Max-Heap using Negative Priorities
- Simple and straightforward implementation
- Works well with numeric values
- No custom class implementation required
- Maintains O(log n) time complexity for operations
- Memory efficient as it only stores the negated values
Drawbacks of Max-Heap using Negative Priorities
- Only works with numeric values
- May cause integer overflow for very large numbers
- Less readable code due to double negation
- Cannot directly view the actual values in the heap without negating them
- Not suitable for complex objects or non-numeric priorities.
2. How to Implement a Max-Heap with a Custom Class using __lt__
?
Implementing a max-heap using a custom class with the __lt__
comparison method allows for a more flexible and object-oriented approach. This method enables the definition of how objects should be compared and sorted within the heap.
class MaxHeap:
def __init__(self):
# Initialize an empty list to act as the heap
self.heap = []
def push(self, value):
# Push elements into the simulated max-heap
heapq.heappush(self.heap, value)
def pop(self):
# Pop the largest element from the heap
return heapq.heappop(self.heap)
def __lt__(self, other):
# Compare two MaxHeap instances based on their heap contents
return self.heap < other.heap
# Example usage
# Create two MaxHeap instances
heap1 = MaxHeap()
heap2 = MaxHeap()
# Push elements into the heaps
heap1.push(5)
heap1.push(1)
heap1.push(8)
heap2.push(3)
heap2.push(2)
heap2.push(9)
# Compare the heaps
print(heap1 < heap2) # This will compare the heaps based on their contents
OutputTrue
The output True
indicates that heap1
is less than heap2
because the comparison is based on the heap contents. In this case, the largest element in heap1
is 8, while the largest element in heap2
is 9. Since 8 is less than 9, heap1
is considered less than heap2
.
Time complexity: O(log n)
for each insertion and extraction operation, where n
is the number of elements in the heap. This is because the heapq.heappush
and heapq.heappop
operations take O(log n)
time.
Space complexity: O(n)
, where n
is the number of elements in the heap. This is because we store all elements in the heap.
Benefits of Max-Heap using Custom Class
- Allows for the use of non-numeric values or complex objects as priorities
- Enables direct comparison of objects without negation
- Provides a more readable and intuitive implementation
- Supports the use of custom comparison logic for objects
Drawbacks of Max-Heap using Custom Class
- Requires a custom class implementation
- May be less efficient for large datasets due to the overhead of object creation and comparison
- Can be more complex to implement and understand for beginners
- May not be suitable for scenarios where numeric values are sufficient and simplicity is preferred.
How to Implement a Priority Queue Using queue.PriorityQueue
?
queue.PriorityQueue
?The queue.PriorityQueue
class is a thread-safe implementation of a priority queue. It is built on top of the heapq
module and provides a more robust and efficient implementation of a priority queue. This allows for the efficient management of tasks with varying priorities in a multi-threaded environment.
Here’s an example of how to use queue.PriorityQueue
to implement a priority queue:
from queue import PriorityQueue
import threading, random, time
# Create a PriorityQueue instance
pq = PriorityQueue()
# Define a worker function that will process tasks from the priority queue
def worker():
while True:
# Get the task with the highest priority from the queue
pri, job = pq.get()
# Process the task
print(f"Processing {job} (pri={pri})")
# Indicate that the task is done
pq.task_done()
# Start a daemon thread that will run the worker function
threading.Thread(target=worker, daemon=True).start()
# Add tasks to the priority queue with random priorities
for job in ["build", "test", "deploy"]:
pq.put((random.randint(1, 10), job))
# Wait for all tasks to be processed
pq.join()
OutputProcessing build (pri=1)
Processing test (pri=2)
Processing deploy (pri=3)
The output demonstrates that the tasks are processed in the order of their priorities, with the highest priority task being processed first. This is achieved by the PriorityQueue
ensuring that the task with the lowest priority number is retrieved first, simulating a priority-based scheduling system.
How does heapq
vs PriorityQueue
compare in multithreading?
heapq
vs PriorityQueue
compare in multithreading?Multithreading is a programming concept where a single program can execute multiple threads or flows of execution concurrently, improving the overall processing efficiency and responsiveness of the system. In a multithreaded environment, multiple threads share the same memory space and resources, which can lead to synchronization issues if not handled properly.
When it comes to implementing priority queues in Python, two popular options are heapq
and PriorityQueue
. Here’s a detailed comparison of these two modules in the context of multithreading:
Feature | heapq |
PriorityQueue |
---|---|---|
Implementation | heapq is not thread-safe, meaning it does not provide built-in mechanisms to ensure safe access and modification in a multithreaded environment. |
PriorityQueue is thread-safe, ensuring that access and modification operations are safely executed in a multithreaded environment. |
Data Structure | heapq uses a list as its underlying data structure. |
PriorityQueue uses a queue as its underlying data structure, which is more suitable for multithreaded applications. |
Complexity | The time complexity of heapq operations is O(n) , where n is the number of elements in the heap. |
The time complexity of PriorityQueue operations is O(log n) , making it more efficient for large datasets. |
Usage | heapq is suitable for single-threaded applications where priority queue operations are not concurrent. |
PriorityQueue is designed for multithreaded applications where concurrent access and modification of the priority queue are necessary. |
Synchronization | Since heapq is not thread-safe, manual synchronization mechanisms are required to ensure thread safety. |
PriorityQueue provides built-in synchronization, eliminating the need for manual synchronization. |
Blocking | heapq does not provide blocking operations, which means that threads may need to implement their own blocking mechanisms. |
PriorityQueue provides blocking operations, allowing threads to wait until a task is available or until all tasks have been completed. |
Task Completion | With heapq , task completion needs to be manually managed by the application. |
PriorityQueue automatically manages task completion, simplifying the development process. |
Priority | heapq does not directly support priority management; priorities need to be implemented manually. |
PriorityQueue supports priority management out of the box, allowing tasks to be prioritized based on their priority. |
Performance | heapq operations are generally faster due to its simpler implementation. |
PriorityQueue operations are slower due to the added complexity of thread safety and synchronization. |
Use Case | heapq is suitable for single-threaded applications where performance is critical and priority queue operations are not concurrent. |
PriorityQueue is ideal for multithreaded applications where thread safety, synchronization, and priority management are essential. |
FAQs
1. What is a priority queue in Python?
A priority queue in Python is a data structure that allows elements to be added and removed based on their priority. It is a type of queue where each element is associated with a priority, and elements are removed in order of their priority. In Python, priority queues can be implemented using the heapq
module or the queue.PriorityQueue
class.
2. How do I implement a priority queue in Python?
There are two common ways to implement a priority queue in Python:
Using heapq
module:
import heapq
# Create a priority queue
pq = []
# Add elements to the priority queue
heapq.heappush(pq, (3, 'task3')) # Priority 3
heapq.heappush(pq, (1, 'task1')) # Priority 1
heapq.heappush(pq, (2, 'task2')) # Priority 2
# Remove elements from the priority queue
while pq:
priority, task = heapq.heappop(pq)
print(f"Priority: {priority}, Task: {task}")
Using queue.PriorityQueue
class:
from queue import PriorityQueue
# Create a priority queue
pq = PriorityQueue()
# Add elements to the priority queue
pq.put((3, 'task3')) # Priority 3
pq.put((1, 'task1')) # Priority 1
pq.put((2, 'task2')) # Priority 2
# Remove elements from the priority queue
while not pq.empty():
priority, task = pq.get()
print(f"Priority: {priority}, Task: {task}")
3. Is Python’s heapq
a min-heap or max-heap?
Python’s heapq
module implements a min-heap by default. This means that the smallest element (based on the priority) is always at the root of the heap. When elements are added or removed, the heap is rebalanced to maintain this property.
You can implement a max-heap by either:
- Inverting priorities (using negative values)
- Implementing a custom class with the
__lt__
comparison method.
Both these methods have been discussed above, so please refer to those sections above.
4. When should I use a priority queue?
A priority queue is particularly useful in scenarios where tasks or elements need to be processed in a specific order based on their priority. Some common use cases include:
- Task scheduling: Prioritize tasks based on their urgency or importance.
- Resource allocation: Allocate resources to tasks or processes based on their priority.
- Event handling: Handle events in the order of their priority, ensuring critical events are processed first.
- Job scheduling: Schedule jobs or tasks in a priority order to ensure efficient resource utilization.
In general, a priority queue is a suitable data structure whenever elements need to be processed in a specific order based on their priority.
5. When to use heapq
?
- In single-threaded applications where performance is critical and priority queue operations are not concurrent.
- When manual synchronization and task completion management are feasible and acceptable.
6. When to use PriorityQueue
?
- In multithreaded applications where thread safety, synchronization, and priority management are essential.
- When built-in synchronization, blocking operations, and automatic task completion management are necessary for efficient and safe concurrent access.
Conclusion
This tutorial has covered the implementation of a priority queue in Python using both heapq
and queue.PriorityQueue
. Additionally, it has explored the creation of a max-heap using these modules.
The comparison of heapq
and PriorityQueue
in the context of multithreading has also been discussed. In summary, heapq
is preferred for single-threaded applications where performance is paramount, while PriorityQueue
is ideal for multithreaded applications where thread safety and synchronization are crucial.
Furthermore, this tutorial has addressed some common questions about priority queues, providing a comprehensive understanding of their usage and implementation in Python.
Next Steps
If you found this tutorial helpful, you may want to check out these other related tutorials:
- JavaScript Binary Heaps: Learn about binary heaps in JavaScript.
- Python Multiprocessing Example: Understand how to use the
multiprocessing
module in Python. - Python Tutorial: A comprehensive tutorial on Python for beginners.
These tutorials cover a wide range of topics and can help you further your understanding of programming and computer science.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
About the author
Helping Businesses stand out with AI, SEO, & Technical content that drives Impact & Growth | Senior Technical Writer @ DigitalOcean | 2x Medium Top Writers | 2 Million+ monthly views & 34K Subscribers | Ex Cloud Engineer @ AMEX | Ex SRE(DevOps) @ NUTANIX
Still looking for an answer?
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
- Table of contents
- Key Takeaways
- Prerequisites
- What Is a Priority Queue?
- How to Implement a Priority Queue Using `heapq`?
- What is a Min-Heap vs Max-Heap?
- How to Implement a Max-Heap using `heapq`?
- How to Implement a Priority Queue Using `queue.PriorityQueue`?
- How does `heapq` vs `PriorityQueue` compare in multithreading?
- FAQs
- Conclusion
Deploy on DigitalOcean
Click below to sign up for DigitalOcean's virtual machines, Databases, and AIML products.
Become a contributor for community
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
DigitalOcean Documentation
Full documentation for every DigitalOcean product.
Resources for startups and SMBs
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Get our newsletter
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
The developer cloud
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Get started for free
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.