KTL
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 
12 namespace 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
binary_heap(const binary_heap &other, const Alloc &allocator) noexcept(std::is_nothrow_copy_constructible_v< T >)
Definition: binary_heap.h:143
binary_heap & operator=(const binary_heap &other) noexcept(std::is_nothrow_copy_assignable_v< T >)
Definition: binary_heap.h:185
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
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
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(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:15