Skip to content

Commit d52bfaf

Browse files
committed
Optimise Day 17
1 parent ec41d7f commit d52bfaf

File tree

2 files changed

+398
-56
lines changed

2 files changed

+398
-56
lines changed

aoclib/prio-queue.hpp

Lines changed: 282 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace aocutil
88
{
9+
910
template<typename T, typename PrioType = int>
1011
class PrioQueue
1112
{
@@ -45,16 +46,30 @@ class PrioQueue
4546
}
4647
}
4748

48-
std::optional<T> extract_min()
49+
T extract_min()
50+
{
51+
auto min_elem = prio_to_elem.begin();
52+
if (min_elem == prio_to_elem.end()) { // Queue is empty.
53+
assert(empty());
54+
throw std::out_of_range("PrioQueue extract_min: Queue already empty");
55+
}
56+
T elem = min_elem->second;
57+
prio_to_elem.erase(min_elem);
58+
elem_to_prio.erase(elem);
59+
return elem;
60+
}
61+
62+
T extract_min(PrioType& prio)
4963
{
5064
auto min_elem = prio_to_elem.begin();
5165
if (min_elem == prio_to_elem.end()) { // Queue is empty.
5266
assert(empty());
53-
return {};
67+
throw std::out_of_range("PrioQueue extract_min: Queue already empty");
5468
}
5569
T elem = min_elem->second;
5670
prio_to_elem.erase(min_elem);
5771
elem_to_prio.erase(elem);
72+
prio = min_elem->first;
5873
return elem;
5974
}
6075

@@ -75,4 +90,269 @@ class PrioQueue
7590
return empt;
7691
}
7792
};
93+
94+
// Is slower...
95+
// template<typename T, typename HashFun = void>
96+
// class BinaryHeap
97+
// {
98+
// enum class Child {Left, Right};
99+
// std::vector<T> nodes;
100+
// using MapType = typename std::conditional_t<std::is_same_v<HashFun, void>, phmap::flat_hash_map<T, std::size_t>, phmap::flat_hash_map<T, std::size_t, HashFun>>;
101+
// MapType elem_to_idx;
102+
103+
// std::size_t get_parent_idx(std::size_t idx) const {
104+
// return (idx - 1) / 2;
105+
// }
106+
107+
// std::optional<std::size_t> get_children_idx(std::size_t idx, Child ct) const
108+
// {
109+
// std::size_t child_idx = 0;
110+
// if (ct == Child::Left) {
111+
// child_idx = 2 * idx + 1;
112+
// } else if (ct == Child::Right) {
113+
// child_idx = 2 * idx + 2;
114+
// } else {
115+
// assert(false);
116+
// }
117+
118+
// if (child_idx < nodes.size()) {
119+
// return child_idx;
120+
// } else {
121+
// return {};
122+
// }
123+
// }
124+
125+
// bool is_ordered(const T& parent, const T& child) const
126+
// {
127+
// if (type == BinaryHeapType::Min) {
128+
// return parent <= child;
129+
// } else if (type == BinaryHeapType::Max) {
130+
// return parent >= child;
131+
// } else {
132+
// throw std::invalid_argument("Binary heap is_ordered: Invalid binary heap type");
133+
// }
134+
// }
135+
136+
137+
// void swap_nodes(std::size_t idx_a, std::size_t idx_b) {
138+
// assert(elem_to_idx.contains(nodes.at(idx_a)));
139+
// assert(elem_to_idx.contains(nodes.at(idx_b)));
140+
141+
// elem_to_idx.at(nodes.at(idx_a)) = idx_b;
142+
// elem_to_idx.at(nodes.at(idx_b)) = idx_a;
143+
// std::swap(nodes.at(idx_a), nodes.at(idx_b));
144+
// }
145+
146+
// void heapify_up(std::size_t idx)
147+
// {
148+
// if (nodes.size() <= 1) {
149+
// return;
150+
// }
151+
152+
// while (idx > 0 && !is_ordered(nodes.at(get_parent_idx(idx)), nodes.at(idx))) {
153+
// swap_nodes(get_parent_idx(idx), idx);
154+
// idx = get_parent_idx(idx);
155+
// }
156+
// }
157+
158+
// void heapify_down(std::size_t idx)
159+
// {
160+
// if (nodes.size() <= 1) {
161+
// return;
162+
// }
163+
// do {
164+
// auto idx_left_child = get_children_idx(idx, Child::Left);
165+
// auto idx_right_child = get_children_idx(idx, Child::Right);
166+
// if (!idx_left_child) {
167+
// assert(!idx_right_child);
168+
// return;
169+
// }
170+
// if (!idx_right_child) {
171+
// idx_right_child = idx_left_child;
172+
// }
173+
174+
// assert(idx_left_child && idx_right_child);
175+
// const T& left = nodes.at(idx_left_child.value());
176+
// const T& right = nodes.at(idx_right_child.value());
177+
// const T& min_max_child = (type == BinaryHeapType::Min) ? std::min(left, right) : std::max(left, right);
178+
// std::size_t min_max_child_idx = min_max_child == left ? idx_left_child.value() : idx_right_child.value();
179+
180+
// if (!is_ordered(nodes.at(idx), nodes.at(min_max_child_idx))) {
181+
// swap_nodes(idx, min_max_child_idx);
182+
// idx = min_max_child_idx;
183+
// } else {
184+
// break;
185+
// }
186+
// } while (idx < nodes.size() - 1);
187+
// }
188+
189+
// bool is_heap() const {
190+
// return (type == BinaryHeapType::Min) ? std::is_heap(nodes.cbegin(), nodes.cend(), std::greater<T>{}) : std::is_heap(nodes.begin(), nodes.end());
191+
// }
192+
193+
// public:
194+
// enum class BinaryHeapType {Max, Min};
195+
// const BinaryHeapType type = BinaryHeapType::Min;
196+
197+
// BinaryHeap(BinaryHeapType type = BinaryHeapType::Min) :type(type) {};
198+
199+
// void insert(const T& elem)
200+
// {
201+
// if (elem_to_idx.contains(elem)) {
202+
// throw "BinaryHeap insert: elem already inside";
203+
// }
204+
205+
// nodes.push_back(elem);
206+
// std::size_t idx = nodes.size() - 1;
207+
// elem_to_idx.insert({elem, idx});
208+
209+
// heapify_up(idx);
210+
// assert(is_heap());
211+
// }
212+
213+
// T extract_min()
214+
// {
215+
// if (size() == 0) {
216+
// throw std::out_of_range("BinaryHeap extract_min: Heap is empty");
217+
// }
218+
// T extracted_elem = nodes.at(0);
219+
// std::size_t last_idx = nodes.size() - 1;
220+
// if (nodes.size() > 1) {
221+
// swap_nodes(0, last_idx);
222+
// }
223+
// nodes.pop_back();
224+
225+
226+
// heapify_down(0);
227+
228+
// elem_to_idx.erase(extracted_elem);
229+
230+
// assert(is_heap());
231+
// return extracted_elem;
232+
// }
233+
234+
// void delete_elem(const T& elem)
235+
// {
236+
237+
// // cf. https://en.wikipedia.org/wiki/Binary_heap#Delete (last retrieved 2024-06-20)
238+
// if (!elem_to_idx.contains(elem)) {
239+
// throw std::out_of_range("BinaryHeap delete_elem: elem not in heap");
240+
// }
241+
// std::size_t deleted_idx = elem_to_idx.at(elem);
242+
// std::size_t last_idx = nodes.size() - 1;
243+
244+
// if (nodes.size() == 1) {
245+
// elem_to_idx.erase(elem);
246+
// nodes.pop_back();
247+
// assert(is_heap());
248+
// return;
249+
// }
250+
251+
// swap_nodes(deleted_idx, last_idx);
252+
// bool require_heapify_up = (type == BinaryHeapType::Min) ? nodes.at(deleted_idx) < nodes.at(last_idx) : nodes.at(deleted_idx) > nodes.at(last_idx) ;
253+
254+
// elem_to_idx.erase(nodes.at(last_idx));
255+
// nodes.pop_back();
256+
// if (nodes.size() == 0) {
257+
// assert(is_heap());
258+
// return;
259+
// }
260+
261+
// if (require_heapify_up) {
262+
// heapify_up(deleted_idx);
263+
// } else {
264+
// heapify_down(deleted_idx);
265+
// }
266+
// assert(is_heap());
267+
// }
268+
269+
// typename std::vector<T>::const_iterator cbegin() const {
270+
// return nodes.cbegin();
271+
// }
272+
273+
// typename std::vector<T>::const_iterator cend() const {
274+
// return nodes.cend();
275+
// }
276+
277+
// std::size_t size() const {
278+
// return nodes.size();
279+
// }
280+
281+
// bool contains(const T& elem) const {
282+
// return elem_to_idx.contains(elem);
283+
// }
284+
// };
285+
286+
// template<typename T, typename PrioType = int>
287+
// class PrioQueueHeap
288+
// {
289+
// private:
290+
// struct KeyVal {
291+
// PrioType key;
292+
// T val;
293+
// auto operator<=>(const KeyVal& other) const {return key - other.key;};
294+
// auto operator==(const KeyVal& other) const {return val == other.val;};
295+
// // KeyVal() = default;
296+
// KeyVal(PrioType key, const T& val) :key(key), val(val) {};
297+
// KeyVal(const T& val) : val(val) {};
298+
299+
300+
// struct KeyValHash {
301+
// std::size_t operator()(const KeyVal& kv) const noexcept {
302+
// return std::hash<T>{}(kv.val);
303+
// }
304+
// };
305+
// };
306+
307+
// BinaryHeap<KeyVal, typename KeyVal::KeyValHash> heap;
308+
309+
// public:
310+
// void insert(const T& elem, const PrioType& priority)
311+
// {
312+
// KeyVal kv(priority, elem);
313+
// if (heap.contains(kv)) {
314+
// throw std::invalid_argument("PrioQueueHeap insert: Element already in queue");
315+
// }
316+
// heap.insert(kv);
317+
// }
318+
319+
// void update_prio(const T& elem, const PrioType& new_priority)
320+
// {
321+
// KeyVal kv(new_priority, elem);
322+
323+
// if (!heap.contains(kv)) {
324+
// throw std::out_of_range("PrioQueueHeap update_prio: Element not in queue.");
325+
// }
326+
// heap.delete_elem(kv);
327+
// heap.insert(kv);
328+
// }
329+
330+
// void insert_or_update(const T& elem, const PrioType& prio)
331+
// {
332+
// if (heap.contains(KeyVal(prio, elem))) {
333+
// update_prio(elem, prio);
334+
// } else {
335+
// insert(elem, prio);
336+
// }
337+
// }
338+
339+
// T extract_min()
340+
// {
341+
// return heap.extract_min().val;
342+
// }
343+
344+
// bool contains(const T& elem) const {
345+
// return heap.contains(KeyVal(elem)); // The key stays undefined but that's okay.
346+
// }
347+
348+
// std::size_t size() const {
349+
// return heap.size();
350+
// }
351+
352+
// bool empty() const
353+
// {
354+
// return heap.size() == 0;
355+
// }
356+
// };
357+
78358
}

0 commit comments

Comments
 (0)