C++ class templates for graph construction and search
The DynamicPriorityQueue
is a critical data structure in the libgraph library that enables efficient graph search algorithms like A* and Dijkstra. Unlike standard priority queues, it supports dynamic priority updates - the ability to change an element’s priority after insertion, which is essential for optimal pathfinding.
In graph search algorithms:
template <typename T, typename Comparator, typename ItemIndexer>
class DynamicPriorityQueue {
private:
std::vector<T> array_; // Binary heap array (index 0 unused)
std::unordered_map<int64_t, std::size_t> element_map_; // Maps element ID to heap position
std::size_t element_num_; // Current number of elements
};
Position 0: Sentinel (unused, simplifies parent comparison)
Position 1: Root (min/max element)
For element at position i:
- Parent: i/2
- Left child: 2*i
- Right child: 2*i + 1
Example Min-Heap:
Array indices: [0] [1] [2] [3] [4] [5] [6]
Array values: [-] [2] [5] [8] [9] [7] [10]
Tree structure:
2 (1)
/ \
5 (2) 8 (3)
/ \ /
9(4) 7(5) 10(6)
void Push(const T& element)
T Pop()
void Update(const T& element)
bool Contains(const T& element)
Before (Buggy):
void DeleteMin() {
if (Empty()) return;
array_[1] = std::move(array_[element_num_--]);
PercolateDown(1);
// BUG: Never removed array_[1] from element_map_!
}
After (Fixed):
void DeleteMin() {
if (Empty()) return;
// Remove the min element from map
element_map_.erase(GetItemIndex(array_[1]));
if (element_num_ > 1) {
array_[1] = std::move(array_[element_num_]);
element_map_[GetItemIndex(array_[1])] = 1;
}
element_num_--;
if (element_num_ > 0) {
PercolateDown(1);
}
}
Impact:
Before (Buggy):
void PercolateUp(const T& element, std::size_t index) {
for (; Compare(element, array_[index / 2]); index /= 2) {
array_[index] = std::move(array_[index / 2]);
// BUG: Never updated element_map_ for moved elements!
}
array_[index] = element;
element_map_[GetItemIndex(element)] = index; // Only final position
}
After (Fixed):
void PercolateUp(const T& element, std::size_t index) {
array_[0] = element; // Sentinel for cleaner loop
while (index > 1 && Compare(element, array_[index / 2])) {
array_[index] = std::move(array_[index / 2]);
element_map_[GetItemIndex(array_[index])] = index; // Update each move
index /= 2;
}
array_[index] = element;
element_map_[GetItemIndex(element)] = index;
}
Impact:
Before (Buggy):
void PercolateDown(std::size_t index) {
// ... moving elements down
array_[index] = std::move(array_[child]);
// BUG: element_map_ not updated for moved elements
}
After (Fixed):
void PercolateDown(std::size_t index) {
T tmp = std::move(array_[index]);
while (index * 2 <= element_num_) {
// ... find smaller child
if (Compare(array_[child], tmp)) {
array_[index] = std::move(array_[child]);
element_map_[GetItemIndex(array_[index])] = index; // Update map
index = child;
} else {
break;
}
}
array_[index] = std::move(tmp);
element_map_[GetItemIndex(array_[index])] = index; // Final position
}
// Custom element with ID for indexing
struct Vertex {
int64_t id;
double cost;
int64_t GetId() const { return id; }
};
// Comparator for min-heap based on cost
struct VertexCompare {
bool operator()(const Vertex& a, const Vertex& b) const {
return a.cost < b.cost; // Min-heap
}
};
// Usage in Dijkstra-like algorithm
DynamicPriorityQueue<Vertex, VertexCompare> pq;
// Initial vertices
pq.Push(Vertex{1, 0.0}); // Start vertex
pq.Push(Vertex{2, INF});
pq.Push(Vertex{3, INF});
// Process vertices
while (!pq.Empty()) {
Vertex current = pq.Pop();
// Process neighbors
for (auto& neighbor : GetNeighbors(current)) {
double new_cost = current.cost + edge_weight;
if (new_cost < neighbor.cost) {
neighbor.cost = new_cost;
pq.Update(neighbor); // Dynamic update!
}
}
}
Operation | Time Complexity | Space Complexity |
---|---|---|
Push (new) | O(log n) | O(1) amortized |
Push (update) | O(log n) | O(1) |
Pop | O(log n) | O(1) |
Update | O(log n) | O(1) |
Contains | O(1) average | O(1) |
Peek | O(1) | O(1) |
Space Usage: O(n) for heap array + O(n) for element map = O(n) total
The implementation includes comprehensive tests:
Compare(element, array_[index/2])
The DynamicPriorityQueue is a carefully designed data structure that enables efficient graph search algorithms. The recent bug fixes ensure:
This implementation represents a production-ready priority queue suitable for real-world graph applications, robotics path planning, and network routing algorithms.