Skip to content

Commit 6eb3186

Browse files
committed
data structure: implement pairing heap without use of shared_ptr
1 parent 6a801e1 commit 6eb3186

File tree

3 files changed

+90
-95
lines changed

3 files changed

+90
-95
lines changed

libopenage/datastructure/pairing_heap.h

+85-91
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,31 +224,30 @@ 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
}
246242

247243
// 0. remove tree root, it's the minimum.
248244
element_t ret = this->root_node;
245+
246+
if (!this->nodes.erase(ret)) {
247+
throw Error{MSG(err) << "didn't remove node"};
248+
}
249+
250+
this->node_count -= 1;
249251
element_t current_sibling = this->root_node->first_child;
250252
this->root_node = nullptr;
251253

@@ -303,49 +305,14 @@ class PairingHeap final {
303305
this->root_node = first_pair->link_backwards();
304306
}
305307

306-
this->node_count -= 1;
307-
308-
#if OPENAGE_PAIRINGHEAP_DEBUG
309-
if (1 != this->nodes.erase(ret)) {
310-
throw Error{ERR << "didn't remove node"};
311-
}
312-
#endif
313-
314308
// (to find those two lines, 14h of debugging passed)
315309
ret->loosen();
316310
ret->first_child = nullptr;
317311

318312
// 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-
}
313+
T data = std::move(ret->data);
314+
delete ret;
315+
return data;
349316
}
350317

351318
/**
@@ -391,14 +358,43 @@ class PairingHeap final {
391358
*
392359
* O(1) (but slower than decrease), and O(pop) when node is the root.
393360
*/
394-
void update(const element_t &node) {
361+
void update(element_t &node) {
395362
if (node != this->root_node) [[likely]] {
396-
this->unlink_node(node);
397-
this->push_node(node);
363+
node = this->push(this->remove_node(node));
398364
}
399365
else {
400366
// it's the root node, so we just pop and push it.
401-
this->push_node(this->pop_node());
367+
node = this->push(this->pop());
368+
}
369+
}
370+
371+
/**
372+
* remove a node from the heap. Return its data.
373+
*
374+
* If the item is the current root, just pop().
375+
* else, cut the node from its parent, pop() that subtree
376+
* and merge these trees.
377+
*
378+
* O(pop_node)
379+
*/
380+
T remove_node(const element_t &node) {
381+
if (node == this->root_node) {
382+
return this->pop();
383+
}
384+
else {
385+
node->loosen();
386+
387+
element_t real_root = this->root_node;
388+
this->root_node = node;
389+
T data = this->pop();
390+
391+
element_t new_root = this->root_node;
392+
this->root_node = real_root;
393+
394+
if (new_root != nullptr) {
395+
this->root_insert(new_root);
396+
}
397+
return data;
402398
}
403399
}
404400

@@ -408,9 +404,10 @@ class PairingHeap final {
408404
void clear() {
409405
this->root_node = nullptr;
410406
this->node_count = 0;
411-
#if OPENAGE_PAIRINGHEAP_DEBUG
407+
for (auto &node : nodes) {
408+
delete node;
409+
}
412410
this->nodes.clear();
413-
#endif
414411
}
415412

416413
/**
@@ -583,7 +580,7 @@ class PairingHeap final {
583580
this->walk_tree(this->root_node, func);
584581
}
585582

586-
protected:
583+
private:
587584
void walk_tree(const element_t &root,
588585
const std::function<void(const element_t &)> &func) const {
589586
func(root);
@@ -607,16 +604,17 @@ class PairingHeap final {
607604
* O(1)
608605
*/
609606
void push_node(const element_t &node) {
610-
this->root_insert(node);
607+
auto [iter, result] = this->nodes.insert(node);
608+
if (result) {
609+
this->root_insert(node);
610+
this->node_count += 1;
611+
}
611612

612613
#if OPENAGE_PAIRINGHEAP_DEBUG
613-
auto ins = this->nodes.insert(node);
614-
if (not ins.second) {
614+
if (not result) {
615615
throw Error{ERR << "node already known"};
616616
}
617617
#endif
618-
619-
this->node_count += 1;
620618
}
621619

622620
/**
@@ -631,14 +629,10 @@ class PairingHeap final {
631629
}
632630
}
633631

634-
protected:
635632
compare cmp;
636633
size_t node_count;
637634
element_t root_node;
638-
639-
#if OPENAGE_PAIRINGHEAP_DEBUG
640635
std::unordered_set<element_t> nodes;
641-
#endif
642636
};
643637

644638
} // namespace openage::datastructure

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)