Heap
Priority Queue
Data Structures
Algorithms
Programming

How to update elements within a heap? priority queue

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Updating priorities inside a heap based priority queue is a common requirement in schedulers, shortest path algorithms, and event systems. The challenge is that heaps provide fast access to top priority but do not support fast random lookup by value. Efficient updates require both heap reordering logic and a way to locate elements quickly.

Understand the Two Update Directions

When an element priority changes, heap repair direction depends on the new value.

For a min heap:

  • Lower key means move up with sift up.
  • Higher key means move down with sift down.

For a max heap, directions are reversed.

If you always run both operations blindly, updates remain correct but waste time. Choosing direction explicitly keeps each update near O(log n).

Keep an Index Map for Fast Element Lookup

A plain heap array cannot locate arbitrary elements quickly. Add a map from item ID to heap index so updates avoid linear search.

Python example using min heap semantics:

python
1class IndexedMinHeap:
2    def __init__(self):
3        self.heap = []               # list of [priority, item]
4        self.pos = {}                # item -> index
5
6    def _swap(self, i, j):
7        self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
8        self.pos[self.heap[i][1]] = i
9        self.pos[self.heap[j][1]] = j
10
11    def _sift_up(self, i):
12        while i > 0:
13            p = (i - 1) // 2
14            if self.heap[p][0] <= self.heap[i][0]:
15                break
16            self._swap(i, p)
17            i = p
18
19    def _sift_down(self, i):
20        n = len(self.heap)
21        while True:
22            l = 2 * i + 1
23            r = 2 * i + 2
24            smallest = i
25
26            if l < n and self.heap[l][0] < self.heap[smallest][0]:
27                smallest = l
28            if r < n and self.heap[r][0] < self.heap[smallest][0]:
29                smallest = r
30
31            if smallest == i:
32                break
33            self._swap(i, smallest)
34            i = smallest
35
36    def push(self, item, priority):
37        if item in self.pos:
38            raise ValueError("item already exists")
39        self.heap.append([priority, item])
40        i = len(self.heap) - 1
41        self.pos[item] = i
42        self._sift_up(i)
43
44    def pop(self):
45        if not self.heap:
46            raise IndexError("empty heap")
47        self._swap(0, len(self.heap) - 1)
48        prio, item = self.heap.pop()
49        del self.pos[item]
50        if self.heap:
51            self._sift_down(0)
52        return item, prio
53
54    def update_priority(self, item, new_priority):
55        i = self.pos[item]
56        old_priority = self.heap[i][0]
57        self.heap[i][0] = new_priority
58
59        if new_priority < old_priority:
60            self._sift_up(i)
61        else:
62            self._sift_down(i)

This design supports insert, pop, and update without scanning the full heap.

Alternative Strategy: Lazy Updates

Some standard library priority queues do not support in place updates. A practical workaround is lazy updates:

  • Push new pair with updated priority.
  • Mark old entry stale in a map.
  • Skip stale entries when popping.

This approach is simple and common in Dijkstra implementations where decrease key is needed but native heap update is unavailable.

python
1import heapq
2
3pq = []
4active = {}
5
6# push current priority
7active["task-1"] = 5
8heapq.heappush(pq, (5, "task-1"))
9
10# update by pushing new value
11active["task-1"] = 2
12heapq.heappush(pq, (2, "task-1"))
13
14while pq:
15    prio, task = heapq.heappop(pq)
16    if active.get(task) == prio:
17        print("next:", task, prio)
18        break

Lazy updates trade some memory and stale entries for implementation simplicity.

Choose the Right Approach

Use indexed heap when:

  • Frequent in place updates are required.
  • Memory overhead of map is acceptable.
  • Deterministic update cost matters.

Use lazy updates when:

  • Update frequency is moderate.
  • Simpler code is preferred.
  • Occasional stale entries are acceptable.

Benchmark both approaches on realistic workload shape before locking in one design.

Common Pitfalls

  • Searching heap linearly for element updates, which turns operations into O(n).
  • Forgetting to update index map during swaps, causing corrupted state.
  • Using wrong repair direction after priority change.
  • Assuming language built in priority queue supports direct key update.
  • Ignoring stale entry accumulation in lazy update patterns.

Summary

  • Heap updates require both element lookup and heap repair.
  • Maintain an index map for efficient in place key changes.
  • Use sift up for higher priority in min heap, sift down for lower priority.
  • Lazy update is a practical fallback when direct update is unavailable.
  • Validate with benchmarks to match design to workload behavior.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.