Flow
Documentation for the Flow C++ Library
Loading...
Searching...
No Matches
flow_binary_heap.h
Go to the documentation of this file.
1#pragma once
3#include "flow_vector.h"
4#include <cassert>
5#include <concepts>
6#include <memory>
7#include <type_traits>
8#include <utility>
9
10namespace flow {
11
13 template <typename T, typename Compare = std::less<T>, typename Allocator = PolymorphicAllocator<T>>
14 class BinaryHeap {
15 public:
16 using value_type = T;
17 using reference = T&;
18 using const_reference = const T&;
19 using pointer = T*;
20 using const_pointer = const T*;
21 using iterator = T*;
22 using const_iterator = const T*;
23 using difference_type = std::ptrdiff_t;
26
27 private:
29 Compare comparator_;
30
31 public:
34 constexpr BinaryHeap()
35 noexcept(noexcept(container_type()) && noexcept(Compare()))
36 requires std::default_initializable<Compare>
37 : data_(), comparator_() {
38 }
39
42 explicit constexpr BinaryHeap(const allocator_type& allocator)
43 noexcept(noexcept(container_type(allocator)) && noexcept(Compare()))
44 requires std::default_initializable<Compare>
45 : data_(allocator), comparator_() {
46 }
47
51 explicit constexpr BinaryHeap(Compare comparator, const allocator_type& allocator = {})
52 noexcept(noexcept(container_type(allocator)) && noexcept(Compare(std::move(comparator))))
53 requires !std::default_initializable<Compare>
54 : data_(allocator), comparator_(std::move(comparator)) {
55 }
56
57 constexpr BinaryHeap(const BinaryHeap&) = default;
58 constexpr BinaryHeap(const BinaryHeap& rhs, const allocator_type& allocator)
59 : data_(rhs.data_, allocator), comparator_(rhs.comparator_) {
60 }
61
62 constexpr BinaryHeap(BinaryHeap&&) = default;
63 constexpr BinaryHeap(BinaryHeap&& rhs, const allocator_type& allocator)
64 noexcept(noexcept(container_type(std::move(rhs.data_), allocator)) && noexcept(Compare(std::move(rhs.comparator_))))
65 : data_(std::move(rhs.data_), allocator), comparator_(std::move(rhs.comparator_)) {
66 }
67
73 template <std::input_iterator It>
74 explicit constexpr BinaryHeap(It first, It last, Compare comparator = {}, const allocator_type& allocator = {})
75 : data_(first, last, allocator), comparator_(std::move(comparator)) {
76 heapify();
77 }
78
83 explicit constexpr BinaryHeap(std::initializer_list<T> list, Compare comparator = {}, const allocator_type& allocator = {})
84 : BinaryHeap(list.begin(), list.end(), std::move(comparator), allocator) {
85 }
86
87 ~BinaryHeap() = default;
88
89 // Allocator.
90 allocator_type get_allocator() const noexcept {
91 return data_.get_allocator();
92 }
93
94 // Accessor.
95
98 constexpr std::size_t size() const noexcept {
99 return data_.size();
100 }
101
104 constexpr std::size_t capacity() const noexcept {
105 return data_.capacity();
106 }
107
110 constexpr bool empty() const noexcept {
111 return data_.empty();
112 }
113
116 const T& top() const noexcept {
117 assert(!data_.empty() && "call top on an empty heap");
118 return data_.front();
119 }
120
122 void clear() noexcept {
123 data_.clear();
124 }
125
128 void reserve(std::size_t capacity) {
129 data_.reserve(capacity);
130 }
131
134 void push(const T& value) {
135 emplace(value);
136 }
137
140 void push(T&& value) {
141 emplace(std::move(value));
142 }
143
146 template <typename ...Args>
147 void emplace(Args&&... args) {
148 data_.emplaceBack(std::forward<Args>(args)...);
149 T value = std::move(data_.back());
150 fixUp(data_.size() - 1, 0, std::move(value));
151 }
152
154 void drop()
155 noexcept(std::is_nothrow_move_assignable_v<T>) {
156 assert(!data_.empty() && "call drop on an empty heap");
157 std::size_t index = fixDownWithHole(0);
158 fixUp(index, 0, std::move(data_.back()));
159 data_.popBack();
160 }
161
164 T pop()
165 noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T>) {
166 assert(!data_.empty() && "call pop on an empty heap");
167 T value = std::move(data_.front());
168 drop();
169 return value;
170 }
171
172 private:
173 // Fix down by repeatedly choosing the less child.
174 // Return the index of the hole at the bottom.
175 std::size_t fixDownWithHole(std::size_t index)
176 noexcept(std::is_nothrow_move_assignable_v<T>) {
177 for (;;) {
178 size_t child = getLeftChild(index);
179 if (child >= data_.size()) {
180 break;
181 }
182
183 // Select the less child.
184 std::size_t rightChild = child + 1;
185 if (rightChild < data_.size() && comparator_(data_[rightChild], data_[child])) {
186 child = rightChild;
187 }
188 data_[index] = std::move(data_[child]);
189 index = child;
190 }
191 return index;
192 }
193
194 // Fix up the value.
195 void fixUp(std::size_t index, std::size_t top, T&& value)
196 noexcept(std::is_nothrow_move_assignable_v<T>) {
197 while (index > top) {
198 std::size_t parent = getParent(index);
199 if (comparator_(value, data_[parent])) {
200 data_[index] = std::move(data_[parent]);
201 index = parent;
202 } else {
203 break;
204 }
205 }
206 data_[index] = std::move(value);
207 }
208
209 void heapify()
210 noexcept(std::is_nothrow_move_constructible_v<T>&& std::is_nothrow_move_assignable_v<T>) {
211 if (data_.size() < 2) {
212 return;
213 }
214 for (std::size_t i = data_.size() / 2; i-- > 0 ;) {
215 T value = std::move(data_[i]);
216 std::size_t hole = fixDownWithHole(i);
217 fixUp(hole, i, std::move(value));
218 }
219 }
220
221 static constexpr bool hasParent(std::size_t i) noexcept {
222 return i > 0;
223 }
224
225 static constexpr std::size_t getParent(std::size_t i) noexcept {
226 assert(i > 0 && "calling parent on the root node");
227 return (i - 1) / 2;
228 }
229
230 static constexpr std::size_t getLeftChild(std::size_t i) noexcept {
231 return i * 2 + 1;
232 }
233 };
234}
constexpr BinaryHeap(Compare comparator, const allocator_type &allocator={}) noexcept(noexcept(container_type(allocator)) &&noexcept(Compare(std::move(comparator))))
Constructs an empty heap with a specific allocator.
constexpr bool empty() const noexcept
Return true if empty.
std::size_t fixDownWithHole(std::size_t index) noexcept(std::is_nothrow_move_assignable_v< T >)
constexpr BinaryHeap(BinaryHeap &&)=default
constexpr BinaryHeap(const BinaryHeap &rhs, const allocator_type &allocator)
constexpr std::size_t capacity() const noexcept
Return the heap capacity.
std::ptrdiff_t difference_type
Vector< T, Allocator > container_type
void heapify() noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_move_assignable_v< T >)
void push(T &&value)
Push the value to the heap.
const T & top() const noexcept
Return the min element.
void clear() noexcept
Clear the heap.
constexpr BinaryHeap(std::initializer_list< T > list, Compare comparator={}, const allocator_type &allocator={})
Initializer list range constructor.
constexpr BinaryHeap(BinaryHeap &&rhs, const allocator_type &allocator) noexcept(noexcept(container_type(std::move(rhs.data_), allocator)) &&noexcept(Compare(std::move(rhs.comparator_))))
const T & const_reference
allocator_type get_allocator() const noexcept
void reserve(std::size_t capacity)
Reserve the heap capacity.
static constexpr std::size_t getLeftChild(std::size_t i) noexcept
constexpr BinaryHeap(const BinaryHeap &)=default
constexpr BinaryHeap() noexcept(noexcept(container_type()) &&noexcept(Compare()))
Constructs an empty heap with default comparator and allocator. Comparator must be default initializa...
static constexpr std::size_t getParent(std::size_t i) noexcept
constexpr BinaryHeap(It first, It last, Compare comparator={}, const allocator_type &allocator={})
Iterator range constructor.
constexpr std::size_t size() const noexcept
Return the heap size.
T pop() noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_move_assignable_v< T >)
Pop the min element.
void push(const T &value)
Push the value to the heap.
~BinaryHeap()=default
container_type data_
void emplace(Args &&... args)
Construct the value inplace in the heap.
container_type::allocator_type allocator_type
static constexpr bool hasParent(std::size_t i) noexcept
constexpr BinaryHeap(const allocator_type &allocator) noexcept(noexcept(container_type(allocator)) &&noexcept(Compare()))
Constructs an empty heap with a specific allocator. Comparator must be default initializable.
void drop() noexcept(std::is_nothrow_move_assignable_v< T >)
Drop the min element.
void fixUp(std::size_t index, std::size_t top, T &&value) noexcept(std::is_nothrow_move_assignable_v< T >)