KTL
Loading...
Searching...
No Matches
binary_heap.h
Go to the documentation of this file.
1#pragma once
2
3#include "../utility/assert.h"
4#include "../utility/empty_base.h"
5#include "binary_heap_fwd.h"
6
7#include <cstddef>
8#include <iterator>
9#include <memory>
10#include <utility>
11
12namespace ktl
13{
20 template<typename T, typename Comp, typename Alloc>
22 {
23 private:
24 static_assert(std::is_move_constructible_v<T>, "T must be move constructible");
25 static_assert(std::is_move_assignable_v<T>, "T must be move assignable");
26
27 static_assert(std::is_default_constructible_v<Alloc> || std::is_copy_constructible_v<Alloc>, "The allocator must be default or copy constructible");
28
29 typedef std::allocator_traits<Alloc> Traits;
30
31 public:
32 typedef T* iterator;
33 typedef const T* const_iterator;
34
35 typedef std::reverse_iterator<T*> reverse_iterator;
36 typedef std::reverse_iterator<const T*> const_reverse_iterator;
37
38 public:
43 binary_heap(const Comp& comp = Comp()) noexcept :
44 m_Alloc(),
45 m_Comp(comp),
46 m_Size(0),
47 m_Capacity(0),
48 m_Begin(nullptr) {}
49
55 binary_heap(const Alloc& allocator, const Comp& comp = Comp()) noexcept :
56 m_Alloc(allocator),
57 m_Comp(comp),
58 m_Size(0),
59 m_Capacity(0),
60 m_Begin(nullptr) {}
61
67 explicit binary_heap(size_t capacity, const Comp& comp = Comp()) noexcept :
68 m_Alloc(),
69 m_Comp(comp),
70 m_Size(0),
71 m_Capacity(capacity),
72 m_Begin(Traits::allocate(m_Alloc, capacity)) {}
73
80 explicit binary_heap(size_t capacity, const Alloc& allocator, const Comp& comp = Comp()) noexcept :
81 m_Alloc(allocator),
82 m_Comp(comp),
83 m_Size(0),
84 m_Capacity(capacity),
85 m_Begin(Traits::allocate(m_Alloc, capacity)) {}
86
92 binary_heap(std::initializer_list<T> initializer, const Comp& comp = Comp()) :
93 m_Alloc(),
94 m_Comp(comp),
95 m_Size(0),
96 m_Capacity(initializer.size()),
97 m_Begin(Traits::allocate(m_Alloc, initializer.size()))
98 {
99 for (auto value : initializer)
100 insert(value);
101 }
102
109 binary_heap(std::initializer_list<T> initializer, const Alloc& allocator, const Comp& comp = Comp()) :
110 m_Alloc(allocator),
111 m_Comp(comp),
112 m_Size(0),
113 m_Capacity(initializer.size()),
114 m_Begin(Traits::allocate(m_Alloc, initializer.size()))
115 {
116 for (auto value : initializer)
117 insert(value);
118 }
119
120 binary_heap(const binary_heap& other) noexcept(std::is_nothrow_copy_constructible_v<T>) :
121 m_Alloc(Traits::select_on_container_copy_construction(other.m_Alloc)),
122 m_Comp(other.m_Comp),
123 m_Size(other.m_Size),
124 m_Capacity(other.m_Size),
125 m_Begin(Traits::allocate(m_Alloc, other.m_Size))
126 {
127 for (size_t i = 0; i < m_Size; i++)
128 Traits::construct(m_Alloc, m_Begin + i, other.m_Begin[i]);
129 }
130
131 binary_heap(binary_heap&& other) noexcept(std::is_nothrow_move_constructible_v<T>) :
132 m_Alloc(std::move(other.m_Alloc)),
133 m_Comp(other.m_Comp),
134 m_Size(other.m_Size),
135 m_Capacity(other.m_Capacity),
136 m_Begin(std::move(other.m_Begin))
137 {
138 other.m_Capacity = 0;
139 other.m_Size = 0;
140 other.m_Begin = nullptr;
141 }
142
143 binary_heap(const binary_heap& other, const Alloc& allocator) noexcept(std::is_nothrow_copy_constructible_v<T>) :
144 m_Alloc(allocator),
145 m_Comp(other.m_Comp),
146 m_Size(other.m_Size),
147 m_Capacity(other.m_Size),
148 m_Begin(Traits::allocate(m_Alloc, other.m_Size))
149 {
150 for (size_t i = 0; i < m_Size; i++)
151 Traits::construct(m_Alloc, m_Begin + i, other.m_Begin[i]);
152 }
153
154 binary_heap(binary_heap&& other, const Alloc& allocator) noexcept(std::is_nothrow_move_constructible_v<T>) :
155 m_Alloc(allocator),
156 m_Comp(std::move(other.m_Comp)),
157 m_Size(other.m_Size),
158 m_Capacity(other.m_Size),
159 m_Begin(Traits::allocate(m_Alloc, other.m_Size))
160 {
161 // Moving using a different allocator means we can't just move, we have to reallocate
162 for (size_t i = 0; i < m_Size; i++)
163 Traits::construct(m_Alloc, m_Begin + i, other.m_Begin[i]);
164
165 if (other.m_Begin != nullptr)
166 Traits::deallocate(other.m_Alloc, other.m_Begin, other.m_Capacity * sizeof(T));
167
168 other.m_Capacity = 0;
169 other.m_Size = 0;
170 other.m_Begin = nullptr;
171 }
172
174 {
175 // Deconstruct elements
176 if (m_Begin)
177 {
178 for (size_t i = 0; i < m_Size; i++)
179 Traits::destroy(m_Alloc, m_Begin + i);
180
181 Traits::deallocate(m_Alloc, m_Begin, m_Capacity);
182 }
183 }
184
185 binary_heap& operator=(const binary_heap& other) noexcept(std::is_nothrow_copy_assignable_v<T>)
186 {
187 if (m_Begin)
188 {
189 for (size_t i = 0; i < m_Size; i++)
190 Traits::destroy(m_Alloc, m_Begin + i);
191
192 Traits::deallocate(m_Alloc, m_Begin, m_Capacity);
193 }
194
195 m_Alloc = other.m_Alloc;
196 m_Comp = other.m_Comp;
197 m_Size = other.m_Size;
198 m_Capacity = other.m_Size;
199 m_Begin = Traits::allocate(m_Alloc, m_Capacity);
200
201 // Copy construct elements
202 for (size_t i = 0; i < m_Size; i++)
203 Traits::construct(m_Alloc, m_Begin + i, other.m_Begin[i]);
204
205 return *this;
206 }
207
208 binary_heap& operator=(binary_heap&& other) noexcept(std::is_nothrow_move_assignable_v<T>)
209 {
210 // Deconstruct elements
211 if (m_Begin)
212 {
213 for (size_t i = 0; i < m_Size; i++)
214 Traits::destroy(m_Alloc, m_Begin + i);
215
216 Traits::deallocate(m_Alloc, m_Begin, m_Capacity);
217 }
218
219 m_Alloc = std::move(other.m_Alloc);
220 m_Comp = std::move(other.m_Comp);
221 m_Size = other.m_Size;
222 m_Capacity = other.m_Capacity;
223 m_Begin = other.m_Begin;
224
225 other.m_Capacity = 0;
226 other.m_Size = 0;
227 other.m_Begin = nullptr;
228
229 return *this;
230 }
231
232
233 iterator begin() noexcept { return m_Begin; }
234
235 const_iterator begin() const noexcept { return m_Begin; }
236
237 iterator end() noexcept { return m_Begin + m_Size; }
238
239 const_iterator end() const noexcept { return m_Begin + m_Size; }
240
241 reverse_iterator rbegin() noexcept { return std::reverse_iterator(m_Begin + m_Size); }
242
243 const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator(m_Begin + m_Size); }
244
245 reverse_iterator rend() noexcept { return std::reverse_iterator(m_Begin); }
246
247 const_reverse_iterator rend() const noexcept { return std::reverse_iterator(m_Begin); }
248
249
254 iterator data() noexcept { return m_Begin; }
255
260 const_iterator data() const noexcept { return m_Begin; }
261
266 size_t size() const noexcept { return m_Size; }
267
272 size_t capacity() const noexcept { return m_Capacity; }
273
278 bool empty() const noexcept { return m_Size == 0; }
279
280
285 void reserve(size_t n) noexcept
286 {
287 if (capacity() < n)
288 set_size(n);
289 }
290
297 template<typename V>
298 iterator insert(V&& value) noexcept
299 {
300 if (m_Size == m_Capacity)
301 expand(1);
302
303 Traits::construct(m_Alloc, m_Begin + m_Size, std::forward<V>(value));
304
305 size_t index = percolateUp(m_Size++);
306
307 return m_Begin + index;
308 }
309
314 T& peek() noexcept
315 {
316 KTL_ASSERT(m_Size > 0);
317
318 return m_Begin[0];
319 }
320
325 T pop() noexcept
326 {
327 KTL_ASSERT(m_Size > 0);
328
329 T root = m_Begin[0];
330
331 m_Begin[0] = std::move(m_Begin[--m_Size]);
332 Traits::destroy(m_Alloc, m_Begin + m_Size);
333 percolateDown(0);
334
335 return root;
336 }
337
343 iterator find(const T& value) const noexcept
344 {
345 for (size_t i = 0; i < m_Size; i++)
346 {
347 if (m_Begin[i] == value) // TODO: Use std::equal_to
348 return m_Begin + i;
349 }
350
351 return m_Begin + m_Size;
352 }
353
357 void clear() noexcept
358 {
359 for (size_t i = 0; i < m_Size; i++)
360 Traits::destroy(m_Alloc, m_Begin + i);
361
362 m_Size = 0;
363 }
364
365 private:
366 void expand(size_t n) noexcept
367 {
368 size_t curCap = capacity();
369 size_t alSize = curCap + (std::max)(curCap, n);
370
371 set_size(alSize);
372 }
373
374 void set_size(size_t n) noexcept
375 {
376 size_t curSize = (std::min)(size(), n);
377
378 T* newData = Traits::allocate(m_Alloc, n);
379
380 if (m_Begin)
381 {
382 // Move construct elements
383 for (size_t i = 0; i < curSize; i++)
384 Traits::construct(m_Alloc, newData + i, std::move(m_Begin[i]));
385
386 // Deconstruct elements
387 for (size_t i = 0; i < m_Size; i++)
388 Traits::destroy(m_Alloc, m_Begin + i);
389
390 // Deallocate old array
391 Traits::deallocate(m_Alloc, m_Begin, m_Capacity);
392 }
393
394 m_Size = curSize;
395 m_Capacity = n;
396 m_Begin = newData;
397 }
398
399 constexpr size_t parent(size_t index) const noexcept
400 {
401 return (index - 1) / 2;
402 }
403
404 constexpr size_t left(size_t index) const noexcept
405 {
406 return index * 2 + 1;
407 }
408
409 constexpr size_t right(size_t index) const noexcept
410 {
411 return index * 2 + 2;
412 }
413
414 size_t percolateUp(size_t index)
415 {
416 const T value = m_Begin[index];
417
418 while (index != 0 && m_Comp(value, m_Begin[parent(index)]))
419 {
420 m_Begin[index] = std::move(m_Begin[parent(index)]);
421 index = parent(index);
422 }
423
424 m_Begin[index] = std::move(value);
425
426 return index;
427 }
428
429 void percolateDown(size_t index) noexcept
430 {
431 size_t parent = index;
432
433 const T value = m_Begin[index];
434
435 while (parent < m_Size)
436 {
437 size_t l = left(parent);
438 size_t r = right(parent);
439
440 size_t child = parent;
441
442 if (l < m_Size && m_Comp(m_Begin[l], m_Begin[child]))
443 child = l;
444
445 if (r < m_Size && m_Comp(m_Begin[r], m_Begin[child]))
446 child = r;
447
448 if (child == parent)
449 break;
450
451 const T temp(std::move(m_Begin[child]));
452 m_Begin[child] = std::move(m_Begin[parent]);
453 m_Begin[parent] = std::move(temp);
454
455 parent = child;
456 }
457
458 m_Begin[parent] = std::move(value);
459 }
460
461 private:
462 KTL_EMPTY_BASE Alloc m_Alloc;
463 KTL_EMPTY_BASE Comp m_Comp;
464
465 size_t m_Size;
466 size_t m_Capacity;
467 T* m_Begin;
468 };
469}
#define KTL_ASSERT(x)
Definition assert.h:17
A priority queue implemented as a binary heap.
Definition binary_heap.h:22
size_t capacity() const noexcept
Returns the current capacity of the heap.
Definition binary_heap.h:272
binary_heap(const Alloc &allocator, const Comp &comp=Comp()) noexcept
Construct the binary heap with the given allocator and comparator.
Definition binary_heap.h:55
std::reverse_iterator< const T * > const_reverse_iterator
Definition binary_heap.h:36
iterator data() noexcept
Returns an iterator to the start of the heap.
Definition binary_heap.h:254
binary_heap(binary_heap &&other) noexcept(std::is_nothrow_move_constructible_v< T >)
Definition binary_heap.h:131
binary_heap(size_t capacity, const Alloc &allocator, const Comp &comp=Comp()) noexcept
Construct the binary heap with the given allocator, comparator and an initial capacity.
Definition binary_heap.h:80
const_reverse_iterator rbegin() const noexcept
Definition binary_heap.h:243
iterator find(const T &value) const noexcept
Returns an iterator to the element index. Takes O(n) time.
Definition binary_heap.h:343
size_t size() const noexcept
Returns the current size of the heap.
Definition binary_heap.h:266
~binary_heap()
Definition binary_heap.h:173
iterator insert(V &&value) noexcept
Inserts a new element into the heap via perfect forwarding.
Definition binary_heap.h:298
reverse_iterator rbegin() noexcept
Definition binary_heap.h:241
binary_heap & operator=(binary_heap &&other) noexcept(std::is_nothrow_move_assignable_v< T >)
Definition binary_heap.h:208
void reserve(size_t n) noexcept
Reserves the capacity of the heap to n, without initializing any elements.
Definition binary_heap.h:285
binary_heap(const binary_heap &other) noexcept(std::is_nothrow_copy_constructible_v< T >)
Definition binary_heap.h:120
const_iterator begin() const noexcept
Definition binary_heap.h:235
iterator begin() noexcept
Definition binary_heap.h:233
T & peek() noexcept
Peeks at the root element (lowest or highest, depending on min or max heap) and returns a reference t...
Definition binary_heap.h:314
binary_heap(const binary_heap &other, const Alloc &allocator) noexcept(std::is_nothrow_copy_constructible_v< T >)
Definition binary_heap.h:143
binary_heap(size_t capacity, const Comp &comp=Comp()) noexcept
Construct the binary heap with the given comparator and an initial capacity.
Definition binary_heap.h:67
const_iterator data() const noexcept
Returns a const iterator to the start of the heap.
Definition binary_heap.h:260
const_reverse_iterator rend() const noexcept
Definition binary_heap.h:247
const_iterator end() const noexcept
Definition binary_heap.h:239
void clear() noexcept
Clear all elements in the heap, destroying each element.
Definition binary_heap.h:357
binary_heap(const Comp &comp=Comp()) noexcept
Construct the binary heap with the given comparator.
Definition binary_heap.h:43
binary_heap & operator=(const binary_heap &other) noexcept(std::is_nothrow_copy_assignable_v< T >)
Definition binary_heap.h:185
iterator end() noexcept
Definition binary_heap.h:237
const T * const_iterator
Definition binary_heap.h:33
binary_heap(std::initializer_list< T > initializer, const Alloc &allocator, const Comp &comp=Comp())
Construct the binary heap with the given allocator, comparator and an initial set of values.
Definition binary_heap.h:109
std::reverse_iterator< T * > reverse_iterator
Definition binary_heap.h:35
bool empty() const noexcept
Returns true if the heap has no elements.
Definition binary_heap.h:278
T * iterator
Definition binary_heap.h:32
reverse_iterator rend() noexcept
Definition binary_heap.h:245
binary_heap(binary_heap &&other, const Alloc &allocator) noexcept(std::is_nothrow_move_constructible_v< T >)
Definition binary_heap.h:154
binary_heap(std::initializer_list< T > initializer, const Comp &comp=Comp())
Construct the binary heap with the given comparator and an initial set of values.
Definition binary_heap.h:92
T pop() noexcept
Removes the root element (lowest or highest, depending on min or max heap) and returns it.
Definition binary_heap.h:325
#define KTL_EMPTY_BASE
Definition empty_base.h:6
Definition cascading.h:16