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<>>
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;
24 using allocator_type = std::allocator_traits<Allocator>::template rebind_alloc<T>;
25
26 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 constexpr BinaryHeap(It first, It last, const allocator_type& allocator = {})
75 : data_(first, last, allocator), comparator_() {
76 heapify();
77 }
78
84 template <std::input_iterator It>
85 constexpr BinaryHeap(It first, It last, Compare comparator, const allocator_type& allocator = {})
86 : data_(first, last, allocator), comparator_(std::move(comparator)) {
87 heapify();
88 }
89
90 ~BinaryHeap() = default;
91
92 // Allocator.
93 allocator_type get_allocator() const noexcept {
94 return data_.get_allocator();
95 }
96
97 // Accessor.
98
101 constexpr std::size_t size() const noexcept {
102 return data_.size();
103 }
104
107 constexpr std::size_t capacity() const noexcept {
108 return data_.capacity();
109 }
110
113 constexpr bool empty() const noexcept {
114 return data_.empty();
115 }
116
119 const T& top() const noexcept {
120 assert(!data_.empty() && "call top on an empty heap");
121 return data_.front();
122 }
123
125 void clear() noexcept {
126 data_.clear();
127 }
128
131 void reserve(std::size_t capacity) {
132 data_.reserve(capacity);
133 }
134
137 void push(const T& value) {
138 emplace(value);
139 }
140
143 void push(T&& value) {
144 emplace(std::move(value));
145 }
146
149 template <typename ...Args>
150 void emplace(Args&&... args) {
151 data_.emplaceBack(std::forward<Args>(args)...);
152 T value = std::move(data_.back());
153 fixUp(data_.size() - 1, 0, std::move(value));
154 }
155
157 void drop()
158 noexcept(std::is_nothrow_move_assignable_v<T>) {
159 assert(!data_.empty() && "call drop on an empty heap");
160 std::size_t index = fixDownWithHole(0);
161 fixUp(index, 0, std::move(data_.back()));
162 data_.popBack();
163 }
164
167 T pop()
168 noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T>) {
169 assert(!data_.empty() && "call pop on an empty heap");
170 T value = std::move(data_.front());
171 drop();
172 return value;
173 }
174
175 private:
176 // Fix down by repeatedly choosing the less child.
177 // Return the index of the hole at the bottom.
178 std::size_t fixDownWithHole(std::size_t index)
179 noexcept(std::is_nothrow_move_assignable_v<T>) {
180 for (;;) {
181 size_t child = getLeftChild(index);
182 if (child >= data_.size()) {
183 break;
184 }
185
186 // Select the less child.
187 std::size_t rightChild = child + 1;
188 if (rightChild < data_.size() && comparator_(data_[rightChild], data_[child])) {
189 child = rightChild;
190 }
191 data_[index] = std::move(data_[child]);
192 index = child;
193 }
194 return index;
195 }
196
197 // Fix up the value.
198 void fixUp(std::size_t index, std::size_t top, T&& value)
199 noexcept(std::is_nothrow_move_assignable_v<T>) {
200 while (index > top) {
201 std::size_t parent = getParent(index);
202 if (comparator_(value, data_[parent])) {
203 data_[index] = std::move(data_[parent]);
204 index = parent;
205 } else {
206 break;
207 }
208 }
209 data_[index] = std::move(value);
210 }
211
212 void heapify()
213 noexcept(std::is_nothrow_move_constructible_v<T>&& std::is_nothrow_move_assignable_v<T>) {
214 if (data_.size() < 2) {
215 return;
216 }
217 for (std::size_t i = data_.size() / 2; i-- > 0 ;) {
218 T value = std::move(data_[i]);
219 std::size_t hole = fixDownWithHole(i);
220 fixUp(hole, i, std::move(value));
221 }
222 }
223
224 static constexpr bool hasParent(std::size_t i) noexcept {
225 return i > 0;
226 }
227
228 static constexpr std::size_t getParent(std::size_t i) noexcept {
229 assert(i > 0 && "calling parent on the root node");
230 return (i - 1) / 2;
231 }
232
233 static constexpr std::size_t getLeftChild(std::size_t i) noexcept {
234 return i * 2 + 1;
235 }
236 };
237}
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(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(It first, It last, Compare comparator, const allocator_type &allocator={})
Iterator range constructor.
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 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_
std::allocator_traits< Allocator >::template rebind_alloc< T > allocator_type
void emplace(Args &&... args)
Construct the value inplace in the heap.
constexpr BinaryHeap(It first, It last, const allocator_type &allocator={})
Iterator range constructor. Comparator must be default initializable.
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 >)