Skip to content

Commit ed26812

Browse files
authored
Merge pull request #1713 from jere8184/paring_heap
data structure: implement pairing heap without use of shared_ptr
2 parents 08fe258 + 5a0c3ec commit ed26812

File tree

3 files changed

+106
-86
lines changed

3 files changed

+106
-86
lines changed

libopenage/datastructure/pairing_heap.h

+101-82
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class PairingHeap;
3838

3939

4040
template <typename T, typename compare = std::less<T>>
41-
class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, compare>> {
41+
class PairingHeapNode {
4242
public:
4343
using this_type = PairingHeapNode<T, compare>;
4444

@@ -47,7 +47,6 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
4747
T data;
4848
compare cmp;
4949

50-
public:
5150
PairingHeapNode(const T &data) :
5251
data{data} {}
5352

@@ -56,6 +55,10 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
5655

5756
~PairingHeapNode() = default;
5857

58+
PairingHeapNode(const this_type &other) = delete;
59+
60+
this_type &operator=(const this_type &other) = delete;
61+
5962
/**
6063
* Get contained node data.
6164
*/
@@ -66,14 +69,14 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
6669
/**
6770
* Let this node become a child of the given one.
6871
*/
69-
void become_child_of(const std::shared_ptr<this_type> &node) {
70-
node->add_child(this->shared_from_this());
72+
void become_child_of(this_type *const node) {
73+
node->add_child(this);
7174
}
7275

7376
/**
7477
* Add the given node as a child to this one.
7578
*/
76-
void add_child(const std::shared_ptr<this_type> &new_child) {
79+
void add_child(this_type *const new_child) {
7780
// first child is the most recently attached one
7881
// it must not have siblings as they will get lost.
7982

@@ -85,31 +88,31 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
8588
}
8689

8790
this->first_child = new_child;
88-
new_child->parent = this->shared_from_this();
91+
new_child->parent = this;
8992
}
9093

9194
/**
9295
* This method decides which node becomes the new root node
9396
* by comparing `this` with `node`.
9497
* The new root is returned, it has the other node as child.
9598
*/
96-
std::shared_ptr<this_type> link_with(const std::shared_ptr<this_type> &node) {
97-
std::shared_ptr<this_type> new_root;
98-
std::shared_ptr<this_type> new_child;
99+
this_type *link_with(this_type *const node) {
100+
this_type *new_root;
101+
this_type *new_child;
99102

100103
if (this->cmp(this->data, node->data)) {
101-
new_root = this->shared_from_this();
104+
new_root = this;
102105
new_child = node;
103106
}
104107
else {
105108
new_root = node;
106-
new_child = this->shared_from_this();
109+
new_child = this;
107110
}
108111

109112
// children of new root become siblings of new new_child
110113
// -> parent of new child = new root
111114

112-
// this whll be set by the add_child method
115+
// this will be set by the add_child method
113116
new_child->prev_sibling = nullptr;
114117
new_child->next_sibling = nullptr;
115118

@@ -128,15 +131,15 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
128131
* Recursive call, one stage for each all childs of the root node.
129132
* This results in the computation of the new subtree root.
130133
*/
131-
std::shared_ptr<this_type> link_backwards() {
134+
this_type *link_backwards() {
132135
if (this->next_sibling == nullptr) {
133136
// reached end, return this as current root,
134137
// the previous siblings will be linked to it.
135-
return this->shared_from_this();
138+
return this;
136139
}
137140

138141
// recurse to last sibling,
139-
std::shared_ptr<this_type> node = this->next_sibling->link_backwards();
142+
this_type *node = this->next_sibling->link_backwards();
140143

141144
// then link ourself to the new root.
142145
this->next_sibling = nullptr;
@@ -153,9 +156,9 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
153156
*/
154157
void loosen() {
155158
// release us from some other node
156-
if (this->parent and this->parent->first_child == this->shared_from_this()) {
157-
// we are the first child
158-
// make the next sibling the first child
159+
if (this->parent and this->parent->first_child == this) {
160+
// we are child
161+
// make the next sibling child
159162
this->parent->first_child = this->next_sibling;
160163
}
161164
// if we have a previous sibling
@@ -176,10 +179,10 @@ class PairingHeapNode : public std::enable_shared_from_this<PairingHeapNode<T, c
176179
}
177180

178181
private:
179-
std::shared_ptr<this_type> first_child;
180-
std::shared_ptr<this_type> prev_sibling;
181-
std::shared_ptr<this_type> next_sibling;
182-
std::shared_ptr<this_type> parent; // for decrease-key and delete
182+
this_type *first_child = nullptr;
183+
this_type *prev_sibling = nullptr;
184+
this_type *next_sibling = nullptr;
185+
this_type *parent = nullptr; // for decrease-key and delete
183186
};
184187

185188

@@ -191,10 +194,8 @@ template <typename T,
191194
typename heapnode_t = PairingHeapNode<T, compare>>
192195
class PairingHeap final {
193196
public:
194-
using node_t = heapnode_t;
195-
using element_t = std::shared_ptr<node_t>;
196-
using this_type = PairingHeap<T, compare, node_t>;
197-
using cmp_t = compare;
197+
using element_t = heapnode_t *;
198+
using this_type = PairingHeap<T, compare, heapnode_t>;
198199

199200
/**
200201
* create a empty heap.
@@ -204,14 +205,16 @@ class PairingHeap final {
204205
root_node(nullptr) {
205206
}
206207

207-
~PairingHeap() = default;
208+
~PairingHeap() {
209+
this->clear();
210+
};
208211

209212
/**
210213
* adds the given item to the heap.
211214
* O(1)
212215
*/
213216
element_t push(const T &item) {
214-
element_t new_node = std::make_shared<node_t>(item);
217+
element_t new_node = new heapnode_t(item);
215218
this->push_node(new_node);
216219
return new_node;
217220
}
@@ -221,25 +224,18 @@ class PairingHeap final {
221224
* O(1)
222225
*/
223226
element_t push(T &&item) {
224-
element_t new_node = std::make_shared<node_t>(std::move(item));
227+
element_t new_node = new heapnode_t(std::move(item));
225228
this->push_node(new_node);
226229
return new_node;
227230
}
228231

229-
/**
230-
* returns and removes the smallest item on the heap.
231-
*/
232-
T pop() {
233-
return std::move(this->pop_node()->data);
234-
}
235-
236232
/**
237233
* returns the smallest item on the heap and deletes it.
238234
* also known as delete_min.
239235
* _________
240236
* Ω(log log n), O(2^(2*√log log n'))
241237
*/
242-
element_t pop_node() {
238+
T pop() {
243239
if (this->root_node == nullptr) {
244240
throw Error{MSG(err) << "Can't pop an empty heap!"};
245241
}
@@ -316,36 +312,9 @@ class PairingHeap final {
316312
ret->first_child = nullptr;
317313

318314
// and it's done!
319-
return ret;
320-
}
321-
322-
/**
323-
* Unlink a node from the heap.
324-
*
325-
* If the item is the current root, just pop().
326-
* else, cut the node from its parent, pop() that subtree
327-
* and merge these trees.
328-
*
329-
* O(pop_node)
330-
*/
331-
void unlink_node(const element_t &node) {
332-
if (node == this->root_node) {
333-
this->pop_node();
334-
}
335-
else {
336-
node->loosen();
337-
338-
element_t real_root = this->root_node;
339-
this->root_node = node;
340-
this->pop_node();
341-
342-
element_t new_root = this->root_node;
343-
this->root_node = real_root;
344-
345-
if (new_root != nullptr) {
346-
this->root_insert(new_root);
347-
}
348-
}
315+
T data = std::move(ret->data);
316+
delete ret;
317+
return data;
349318
}
350319

351320
/**
@@ -391,21 +360,52 @@ class PairingHeap final {
391360
*
392361
* O(1) (but slower than decrease), and O(pop) when node is the root.
393362
*/
394-
void update(const element_t &node) {
363+
void update(element_t &node) {
395364
if (node != this->root_node) [[likely]] {
396-
this->unlink_node(node);
397-
this->push_node(node);
365+
node = this->push(this->remove_node(node));
398366
}
399367
else {
400368
// it's the root node, so we just pop and push it.
401-
this->push_node(this->pop_node());
369+
node = this->push(this->pop());
370+
}
371+
}
372+
373+
/**
374+
* remove a node from the heap. Return its data.
375+
*
376+
* If the item is the current root, just pop().
377+
* else, cut the node from its parent, pop() that subtree
378+
* and merge these trees.
379+
*
380+
* O(pop_node)
381+
*/
382+
T remove_node(const element_t &node) {
383+
if (node == this->root_node) {
384+
return this->pop();
385+
}
386+
else {
387+
node->loosen();
388+
389+
element_t real_root = this->root_node;
390+
this->root_node = node;
391+
T data = this->pop();
392+
393+
element_t new_root = this->root_node;
394+
this->root_node = real_root;
395+
396+
if (new_root != nullptr) {
397+
this->root_insert(new_root);
398+
}
399+
return data;
402400
}
403401
}
404402

405403
/**
406404
* erase all elements on the heap.
407405
*/
408406
void clear() {
407+
auto delete_node = [](element_t node) { delete node; };
408+
this->iter_all<true>(delete_node);
409409
this->root_node = nullptr;
410410
this->node_count = 0;
411411
#if OPENAGE_PAIRINGHEAP_DEBUG
@@ -579,39 +579,59 @@ class PairingHeap final {
579579
}
580580
#endif
581581

582+
/**
583+
* Apply the given function to all nodes in the tree.
584+
*
585+
* @tparam reverse If true, the function is applied to the nodes in reverse order.
586+
* @param func Function to apply to each node.
587+
*/
588+
template <bool reverse = false>
582589
void iter_all(const std::function<void(const element_t &)> &func) const {
583-
this->walk_tree(this->root_node, func);
590+
this->walk_tree<reverse>(this->root_node, func);
584591
}
585592

586-
protected:
587-
void walk_tree(const element_t &root,
593+
private:
594+
/**
595+
* Apply the given function to all nodes in the tree.
596+
*
597+
* @tparam reverse If true, the function is applied to the nodes in reverse order.
598+
* @param start Starting node.
599+
* @param func Function to apply to each node.
600+
*/
601+
template <bool reverse = false>
602+
void walk_tree(const element_t &start,
588603
const std::function<void(const element_t &)> &func) const {
589-
func(root);
604+
if constexpr (not reverse) {
605+
func(start);
606+
}
590607

591-
if (root) {
592-
auto node = root->first_child;
608+
if (start) {
609+
auto node = start->first_child;
593610
while (true) {
594611
if (not node) {
595612
break;
596613
}
597614

598-
this->walk_tree(node, func);
615+
this->walk_tree<reverse>(node, func);
599616
node = node->next_sibling;
600617
}
618+
if constexpr (reverse) {
619+
func(start);
620+
}
601621
}
602622
}
603623

624+
604625
/**
605626
* adds the given node to the heap.
606627
* use this if the node was not in the heap before.
607628
* O(1)
608629
*/
609630
void push_node(const element_t &node) {
610631
this->root_insert(node);
611-
612632
#if OPENAGE_PAIRINGHEAP_DEBUG
613-
auto ins = this->nodes.insert(node);
614-
if (not ins.second) {
633+
auto [iter, result] = this->nodes.insert(node);
634+
if (not result) {
615635
throw Error{ERR << "node already known"};
616636
}
617637
#endif
@@ -631,7 +651,6 @@ class PairingHeap final {
631651
}
632652
}
633653

634-
protected:
635654
compare cmp;
636655
size_t node_count;
637656
element_t root_node;

libopenage/datastructure/tests.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014-2023 the openage authors. See copying.md for legal info.
1+
// Copyright 2014-2024 the openage authors. See copying.md for legal info.
22

33
#include "tests.h"
44

@@ -118,7 +118,8 @@ void pairing_heap_2() {
118118
heap.push(heap_elem{3});
119119

120120
// state: 1 2 3, now remove 2
121-
heap.unlink_node(node);
121+
auto data = heap.remove_node(node);
122+
TESTEQUALS(data.data, 2);
122123

123124
// state: 1 3
124125
TESTEQUALS(heap.pop().data, 1);

libopenage/event/eventstore.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018-2023 the openage authors. See copying.md for legal info.
1+
// Copyright 2018-2024 the openage authors. See copying.md for legal info.
22

33
#include "eventstore.h"
44

@@ -56,7 +56,7 @@ bool EventStore::erase(const std::shared_ptr<Event> &event) {
5656
bool erased = false;
5757
auto it = this->events.find(event);
5858
if (it != std::end(this->events)) {
59-
this->heap.unlink_node(it->second);
59+
this->heap.remove_node(it->second);
6060
this->events.erase(it);
6161
erased = true;
6262
}

0 commit comments

Comments
 (0)