Clustering
C++20 header-only: DBSCAN, HDBSCAN, k-means.
Loading...
Searching...
No Matches
heap.h
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <cstddef>
5#include <cstdint>
6#include <limits>
7#include <type_traits>
8#include <utility>
9#include <vector>
10
11#include "clustering/math/detail/heap_ops.h"
12
13namespace clustering {
14
25template <class Key, class Val> class BinaryHeap {
26public:
27 BinaryHeap() = default;
28
37 void push(Key key, Val val) {
38 m_heap.emplace_back(std::move(key), std::move(val));
39 math::detail::siftUp(m_heap, m_heap.size() - 1, minOnKey);
40 }
41
49 [[nodiscard]] const std::pair<Key, Val> &top() const noexcept {
50 assert(!m_heap.empty() && "BinaryHeap::top on empty heap");
51 return m_heap.front();
52 }
53
60 void pop() noexcept {
61 assert(!m_heap.empty() && "BinaryHeap::pop on empty heap");
62 const std::size_t last = m_heap.size() - 1;
63 if (last != 0) {
64 m_heap[0] = std::move(m_heap[last]);
65 }
66 m_heap.pop_back();
67 if (!m_heap.empty()) {
68 math::detail::siftDown(m_heap, 0, minOnKey);
69 }
70 }
71
77 [[nodiscard]] std::size_t size() const noexcept { return m_heap.size(); }
78
84 [[nodiscard]] bool empty() const noexcept { return m_heap.empty(); }
85
86private:
88 static bool minOnKey(const std::pair<Key, Val> &a, const std::pair<Key, Val> &b) noexcept {
89 return a.first < b.first;
90 }
91
92 std::vector<std::pair<Key, Val>> m_heap;
93};
94
109template <class Key, class Val, class Idx = std::uint32_t> class IndexedHeap {
110public:
111 static_assert(std::is_unsigned_v<Idx>, "IndexedHeap Idx must be an unsigned integer type");
112
114 static constexpr std::size_t kNotInHeap = std::numeric_limits<std::size_t>::max();
115
121 struct Entry {
125 Key key;
127 Val val;
128 };
129
137 explicit IndexedHeap(std::size_t capacity) : m_posMap(capacity, kNotInHeap) {}
138
149 void push(Idx handle, Key key, Val val) {
150 assert(static_cast<std::size_t>(handle) < m_posMap.size() &&
151 "IndexedHeap::push handle out of range");
152 assert(m_posMap[handle] == kNotInHeap && "IndexedHeap::push handle already in heap");
153 const std::size_t pos = m_heap.size();
154 m_heap.push_back(Entry{handle, std::move(key), std::move(val)});
155 m_posMap[handle] = pos;
156 siftUp(pos);
157 }
158
165 [[nodiscard]] bool contains(Idx handle) const noexcept {
166 assert(static_cast<std::size_t>(handle) < m_posMap.size() &&
167 "IndexedHeap::contains handle out of range");
168 return m_posMap[handle] != kNotInHeap;
169 }
170
181 void decreaseKey(Idx handle, Key newKey) noexcept {
182 assert(static_cast<std::size_t>(handle) < m_posMap.size() &&
183 "IndexedHeap::decreaseKey handle out of range");
184 const std::size_t pos = m_posMap[handle];
185 assert(pos != kNotInHeap && "IndexedHeap::decreaseKey handle not in heap");
186 assert(!(m_heap[pos].key < newKey) && "IndexedHeap::decreaseKey newKey > current key");
187 m_heap[pos].key = std::move(newKey);
188 siftUp(pos);
189 }
190
198 [[nodiscard]] const Entry &top() const noexcept {
199 assert(!m_heap.empty() && "IndexedHeap::top on empty heap");
200 return m_heap.front();
201 }
202
210 Entry pop() noexcept {
211 assert(!m_heap.empty() && "IndexedHeap::pop on empty heap");
212 Entry out = std::move(m_heap.front());
213 m_posMap[out.handle] = kNotInHeap;
214 const std::size_t last = m_heap.size() - 1;
215 if (last != 0) {
216 m_heap.front() = std::move(m_heap[last]);
217 m_posMap[m_heap.front().handle] = 0;
218 }
219 m_heap.pop_back();
220 if (!m_heap.empty()) {
221 siftDown(0);
222 }
223 return out;
224 }
225
231 [[nodiscard]] std::size_t size() const noexcept { return m_heap.size(); }
232
238 [[nodiscard]] bool empty() const noexcept { return m_heap.empty(); }
239
245 [[nodiscard]] std::size_t capacity() const noexcept { return m_posMap.size(); }
246
247private:
248 std::vector<Entry> m_heap;
249 std::vector<std::size_t> m_posMap;
250
251 void swapEntries(std::size_t i, std::size_t j) noexcept {
252 std::swap(m_heap[i], m_heap[j]);
253 m_posMap[m_heap[i].handle] = i;
254 m_posMap[m_heap[j].handle] = j;
255 }
256
257 void siftUp(std::size_t pos) noexcept {
258 while (pos > 0) {
259 const std::size_t parent = (pos - 1) / 2;
260 if (m_heap[pos].key < m_heap[parent].key) {
261 swapEntries(pos, parent);
262 pos = parent;
263 } else {
264 return;
265 }
266 }
267 }
268
269 void siftDown(std::size_t pos) noexcept {
270 const std::size_t n = m_heap.size();
271 while (true) {
272 const std::size_t left = (2 * pos) + 1;
273 const std::size_t right = left + 1;
274 std::size_t smallest = pos;
275 if (left < n && m_heap[left].key < m_heap[smallest].key) {
276 smallest = left;
277 }
278 if (right < n && m_heap[right].key < m_heap[smallest].key) {
279 smallest = right;
280 }
281 if (smallest == pos) {
282 return;
283 }
284 swapEntries(pos, smallest);
285 pos = smallest;
286 }
287 }
288};
289
290} // namespace clustering
const std::pair< Key, Val > & top() const noexcept
Smallest-key entry currently in the heap.
Definition heap.h:49
std::size_t size() const noexcept
Current number of entries.
Definition heap.h:77
void pop() noexcept
Remove the smallest-key entry.
Definition heap.h:60
bool empty() const noexcept
Whether the heap holds zero entries.
Definition heap.h:84
void push(Key key, Val val)
Insert a new (key, val) entry.
Definition heap.h:37
bool contains(Idx handle) const noexcept
Whether handle is currently present in the heap.
Definition heap.h:165
std::size_t capacity() const noexcept
Maximum handle + 1 (the capacity passed at construction).
Definition heap.h:245
void push(Idx handle, Key key, Val val)
Insert an entry for handle.
Definition heap.h:149
static constexpr std::size_t kNotInHeap
Sentinel for "handle not currently in the heap". Equal to size_t's max.
Definition heap.h:114
void decreaseKey(Idx handle, Key newKey) noexcept
Lower the key of handle's entry to newKey.
Definition heap.h:181
IndexedHeap(std::size_t capacity)
Construct an empty heap capable of handles in [0, capacity).
Definition heap.h:137
std::size_t size() const noexcept
Current number of entries.
Definition heap.h:231
bool empty() const noexcept
Whether the heap holds zero entries.
Definition heap.h:238
const Entry & top() const noexcept
Smallest-key entry currently in the heap.
Definition heap.h:198
Entry pop() noexcept
Remove and return the smallest-key entry.
Definition heap.h:210
Idx handle
External identity supplied by the caller at push time.
Definition heap.h:123
Val val
Payload associated with handle.
Definition heap.h:127
Key key
Ordering key; the heap root carries the smallest key.
Definition heap.h:125