CS205 Data Structures
Use arrow keys or buttons to navigate · Press Home/End to jump
A collection where each element has a priority. The element with the highest priority (lowest key) is served first -- not the one that arrived first.
Whoever arrives first, leaves first.
Lowest key (highest priority) leaves first, regardless of arrival.
Patients are not seen in arrival order. The most critical case (lowest priority number) is treated first.
A priority queue is an ADT that supports inserting elements with keys and removing the element whose key is minimal (or maximal, depending on convention).
A priority queue stores a collection of entries, each a (key, value) pair.
| Method | Description |
|---|---|
insert(k, v) | Insert entry with key k and value v |
removeMin() | Remove & return entry with smallest key |
min() | Return (but don't remove) entry with smallest key |
size() | Return number of entries |
isEmpty() | Is the PQ empty? |
removeMin() and min() throw an error (or return null) if called on an empty priority queue. Always check isEmpty() first.
Keys define priority. But how do we compare them?
A comparison rule must satisfy:
Any two keys must be comparable. This is what makes it a total order (as opposed to a partial order).
| Approach | Example |
|---|---|
| Natural | Integers: 1 < 2 < 3 ... |
| Custom | Strings by length, Points by x-coord |
A comparator is like a judge at a competition -- you can swap in a different judge to rank contestants by a different criterion.
Store entries in an unsorted linked list or array. Insertion is fast, but finding the minimum requires scanning.
| Operation | Time |
|---|---|
insert | O(1) |
removeMin | O(n) |
min | O(n) |
size, isEmpty | O(1) |
Insert is O(1) because we just append. But removeMin is O(n) because we must scan every entry to find the smallest key.
Good for insert-heavy workloads. Terrible if you remove frequently.
Keep entries sorted by key. The minimum is always at the front, but insertion requires finding the right position.
| Operation | Time |
|---|---|
insert | O(n) |
removeMin | O(1) |
min | O(1) |
size, isEmpty | O(1) |
removeMin is O(1) because the smallest key is always at the front. But insert is O(n) because we walk the list to find the sorted position.
Good for remove-heavy workloads. Terrible if you insert frequently.
Neither list-based approach gives us the best of both worlds.
| Operation | Unsorted List | Sorted List |
|---|---|---|
insert(k, v) |
O(1) | O(n) |
removeMin() |
O(n) | O(1) |
min() |
O(n) | O(1) |
Unsorted list = throwing clothes into a pile. Fast to add, slow to find what you need.
Sorted list = keeping a perfectly organized closet. Slow to put away, fast to grab the right item.
Can we get O(log n) for both insert and removeMin? Yes -- with a heap! (Coming in the next lecture.)
Use an unsorted list PQ to sort a sequence.
insert is O(1)removeMin() n times — each is O(n), O(n-1), ... O(1)This is exactly Selection Sort! Each removeMin "selects" the minimum from the remaining unsorted elements.
O(n^2) in all cases -- no best-case improvement. The scan is always required.
Use a sorted list PQ to sort a sequence.
insert walks the sorted listremoveMin() n times — each is O(1)This is exactly Insertion Sort! Each insert places the element into its correct sorted position.
Like sorting a hand of playing cards: you pick up each card and slide it into the right spot among the cards you're already holding.
Both PQ-based sorts are O(n^2). Is there a middle ground?
A binary heap achieves O(log n) for both insert and removeMin by using a complete binary tree with the heap-order property.
Priority queues store entries, not bare keys. Each entry is a (key, value) pair.
A boarding pass at an airport: the key is your boarding group number, the value is you (the passenger). The airline decides your priority, not your name.
| Context | Key | Value |
|---|---|---|
| ER Triage | Severity | Patient |
| Print Queue | Priority | Print Job |
| Event Sim | Time | Event |
| Dijkstra's | Distance | Vertex |
| Huffman | Frequency | Tree Node |
Entries decouple "what you store" from "how it's prioritized." This makes the PQ reusable across many domains.
Sometimes you need to change a key or remove an arbitrary entry -- not just the minimum.
| Method | Description |
|---|---|
remove(e) | Remove entry e from PQ |
replaceKey(e, k) | Change key of entry e to k |
replaceValue(e, v) | Change value of entry e to v |
The locator lets us jump directly to an entry in O(1), avoiding an O(n) search.
Without location-aware entries, replaceKey would require O(n) to find the entry first. With a locator, it's O(1) to find + O(log n) to re-heapify = O(log n) total.
An adaptable PQ extends the standard PQ by letting you modify entries already in the queue. This is essential for graph algorithms like Dijkstra's and Prim's.
Operating systems use priority queues to decide which process runs next.
Like a hospital with one doctor: the most critical patient always gets treated next, but new patients can arrive at any time and get triaged into the queue.
The PQ efficiently finds the next process to schedule. With a heap, this costs O(log n) per operation -- critical when the OS handles thousands of processes.
Simulate a system by processing events in chronological order, even if they were generated out of order.
Events are entries with time as the key. Processing an event often creates new events (inserted with future timestamps). The PQ always gives us the chronologically next event.
Like a director with a schedule of scenes to film. Each scene might add new scenes to the schedule. The PQ always picks the scene that happens earliest in the story timeline.
The priority queue is the engine behind the famous shortest-path algorithm.
Dijkstra's "greedily" picks the unvisited vertex with the smallest known distance using removeMin(). With a heap-based PQ, the algorithm runs in O((V + E) log V).
Prerequisite You'll study this in detail in the Graph Algorithms unit.
Build optimal prefix-free codes for data compression using a priority queue.
Frequent characters get short codes; rare characters get long codes.
The PQ always merges the two least frequent nodes. This greedy strategy produces an optimal prefix code. Two removeMin + one insert per step.
How do all PQ implementations stack up?
| Operation | Unsorted List | Sorted List | Binary Heap | BST (balanced) |
|---|---|---|---|---|
insert |
O(1) | O(n) | O(log n) | O(log n) |
removeMin |
O(n) | O(1) | O(log n) | O(log n) |
min |
O(n) | O(1) | O(1) | O(log n) |
| PQ-Sort | O(n^2) | O(n^2) | O(n log n) | O(n log n) |
| Space | O(n) | O(n) | O(n) | O(n) |
The binary heap is the sweet spot for priority queues: O(log n) insert and removeMin, O(1) min, simple array-based storage, and it gives us Heap Sort at O(n log n).
A balanced BST (like AVL or Red-Black) also achieves O(log n) for everything, but has higher constant factors and more complex implementation. Heaps are preferred when you only need PQ operations.
| Unsorted | Sorted | Heap | |
|---|---|---|---|
insert | O(1) | O(n) | O(log n) |
removeMin | O(n) | O(1) | O(log n) |
min | O(n) | O(1) | O(1) |
List-based PQs are simple but O(n) for at least one core operation. The binary heap (next lecture!) achieves O(log n) for both insert and removeMin -- the gold standard for priority queues.
Next up: Binary Heaps -- the data structure that makes PQs efficient.