From 197c78fa4e07cf7896a566263a60f974c5093a9a Mon Sep 17 00:00:00 2001 From: Jeremiah Morgan Date: Thu, 7 Nov 2024 23:11:12 +0000 Subject: [PATCH] data structure: mplement pairing heap without shared_ptr --- libopenage/datastructure/pairing_heap.h | 301 +++++++++++++----------- 1 file changed, 158 insertions(+), 143 deletions(-) diff --git a/libopenage/datastructure/pairing_heap.h b/libopenage/datastructure/pairing_heap.h index dd5438091a..3fd81ee5cc 100644 --- a/libopenage/datastructure/pairing_heap.h +++ b/libopenage/datastructure/pairing_heap.h @@ -38,7 +38,7 @@ class PairingHeap; template > -class PairingHeapNode : public std::enable_shared_from_this> { +class PairingHeapNode{ public: using this_type = PairingHeapNode; @@ -47,7 +47,6 @@ class PairingHeapNode : public std::enable_shared_from_this &node) { - node->add_child(this->shared_from_this()); + void become_child_of(this_type* const node) { + node->add_child(this); } /** * Add the given node as a child to this one. */ - void add_child(const std::shared_ptr &new_child) { + void add_child(this_type* const new_child) { // first child is the most recently attached one // it must not have siblings as they will get lost. new_child->prev_sibling = nullptr; - new_child->next_sibling = this->first_child; + new_child->next_sibling = this->child; - if (this->first_child != nullptr) { - this->first_child->prev_sibling = new_child; + if (this->child != nullptr) { + this->child->prev_sibling = new_child; } - this->first_child = new_child; - new_child->parent = this->shared_from_this(); + this->child = new_child; + new_child->parent = this; } /** @@ -93,23 +96,23 @@ class PairingHeapNode : public std::enable_shared_from_this link_with(const std::shared_ptr &node) { - std::shared_ptr new_root; - std::shared_ptr new_child; + this_type* link_with(this_type* const node) { + this_type* new_root; + this_type* new_child; if (this->cmp(this->data, node->data)) { - new_root = this->shared_from_this(); + new_root = this; new_child = node; } else { new_root = node; - new_child = this->shared_from_this(); + new_child = this; } // children of new root become siblings of new new_child // -> parent of new child = new root - // this whll be set by the add_child method + // this will be set by the add_child method new_child->prev_sibling = nullptr; new_child->next_sibling = nullptr; @@ -128,15 +131,15 @@ class PairingHeapNode : public std::enable_shared_from_this link_backwards() { + this_type* link_backwards() { if (this->next_sibling == nullptr) { // reached end, return this as current root, // the previous siblings will be linked to it. - return this->shared_from_this(); + return this; } // recurse to last sibling, - std::shared_ptr node = this->next_sibling->link_backwards(); + this_type* node = this->next_sibling->link_backwards(); // then link ourself to the new root. this->next_sibling = nullptr; @@ -153,10 +156,10 @@ class PairingHeapNode : public std::enable_shared_from_thisparent and this->parent->first_child == this->shared_from_this()) { - // we are the first child - // make the next sibling the first child - this->parent->first_child = this->next_sibling; + if (this->parent and this->parent->child == this) { + // we are child + // make the next sibling child + this->parent->child = this->next_sibling; } // if we have a previous sibling if (this->prev_sibling != nullptr) { @@ -176,10 +179,10 @@ class PairingHeapNode : public std::enable_shared_from_this first_child; - std::shared_ptr prev_sibling; - std::shared_ptr next_sibling; - std::shared_ptr parent; // for decrease-key and delete + this_type* child = nullptr; + this_type* prev_sibling = nullptr; + this_type* next_sibling = nullptr; + this_type* parent = nullptr; // for decrease-key and delete }; @@ -191,10 +194,8 @@ template > class PairingHeap final { public: - using node_t = heapnode_t; - using element_t = std::shared_ptr; - using this_type = PairingHeap; - using cmp_t = compare; + using element_t = heapnode_t*; + using this_type = PairingHeap; /** * create a empty heap. @@ -204,14 +205,17 @@ class PairingHeap final { root_node(nullptr) { } - ~PairingHeap() = default; + ~PairingHeap() + { + this->clear(); + }; /** * adds the given item to the heap. * O(1) */ element_t push(const T &item) { - element_t new_node = std::make_shared(item); + element_t new_node = new heapnode_t(item); this->push_node(new_node); return new_node; } @@ -221,7 +225,7 @@ class PairingHeap final { * O(1) */ element_t push(T &&item) { - element_t new_node = std::make_shared(std::move(item)); + element_t new_node = new heapnode_t(std::move(item)); this->push_node(new_node); return new_node; } @@ -230,93 +234,10 @@ class PairingHeap final { * returns and removes the smallest item on the heap. */ T pop() { - return std::move(this->pop_node()->data); - } - - /** - * returns the smallest item on the heap and deletes it. - * also known as delete_min. - * _________ - * Ω(log log n), O(2^(2*√log log n')) - */ - element_t pop_node() { - if (this->root_node == nullptr) { - throw Error{MSG(err) << "Can't pop an empty heap!"}; - } - - // 0. remove tree root, it's the minimum. - element_t ret = this->root_node; - element_t current_sibling = this->root_node->first_child; - this->root_node = nullptr; - - // 1. link root children pairwise, last node may be alone - element_t first_pair = nullptr; - element_t previous_pair = nullptr; - - while (current_sibling != nullptr) [[unlikely]] { - element_t link0 = current_sibling; - element_t link1 = current_sibling->next_sibling; - - // pair link0 and link1 - if (link1 != nullptr) { - // get the first sibling for next pair, just in advance. - current_sibling = link1->next_sibling; - - // do the link: merges two nodes, smaller one = root. - element_t link_root = link0->link_with(link1); - link_root->parent = nullptr; - - if (previous_pair == nullptr) { - // this was the first pair - first_pair = link_root; - first_pair->prev_sibling = nullptr; - } - else { - // store node as next sibling in previous pair - previous_pair->next_sibling = link_root; - link_root->prev_sibling = previous_pair; - } - - previous_pair = link_root; - link_root->next_sibling = nullptr; - } - else { - // link0 is the last and unpaired root child. - link0->parent = nullptr; - if (previous_pair == nullptr) { - // link0 was the only node - first_pair = link0; - link0->prev_sibling = nullptr; - } - else { - previous_pair->next_sibling = link0; - link0->prev_sibling = previous_pair; - } - link0->next_sibling = nullptr; - current_sibling = nullptr; - } - } - - - // 2. then link remaining trees to the last one, from right to left - if (first_pair != nullptr) { - this->root_node = first_pair->link_backwards(); - } - - this->node_count -= 1; - -#if OPENAGE_PAIRINGHEAP_DEBUG - if (1 != this->nodes.erase(ret)) { - throw Error{ERR << "didn't remove node"}; - } -#endif - - // (to find those two lines, 14h of debugging passed) - ret->loosen(); - ret->first_child = nullptr; - - // and it's done! - return ret; + element_t poped_node = this->pop_node(); + T data = std::move(poped_node->data); + delete poped_node; + return data; } /** @@ -328,7 +249,7 @@ class PairingHeap final { * * O(pop_node) */ - void unlink_node(const element_t &node) { + void unlink_node(const element_t &node) { //how can we garuntee poped node deleted, without use of shared ptr, or making this function private? if (node == this->root_node) { this->pop_node(); } @@ -393,7 +314,7 @@ class PairingHeap final { */ void update(const element_t &node) { if (node != this->root_node) [[likely]] { - this->unlink_node(node); + this->unlink_node(node); this->push_node(node); } else { @@ -408,8 +329,13 @@ class PairingHeap final { void clear() { this->root_node = nullptr; this->node_count = 0; -#if OPENAGE_PAIRINGHEAP_DEBUG + for (auto& node : nodes) + { + delete node; + } this->nodes.clear(); + +#if OPENAGE_PAIRINGHEAP_DEBUG #endif } @@ -470,15 +396,15 @@ class PairingHeap final { } } - if (root->first_child) { - if (root->first_child == root->next_sibling) { - throw Error{ERR << "first_child is next_sibling"}; + if (root->child) { + if (root->child == root->next_sibling) { + throw Error{ERR << "child is next_sibling"}; } - if (root->first_child == root->prev_sibling) { - throw Error{ERR << "first_child is prev_sibling"}; + if (root->child == root->prev_sibling) { + throw Error{ERR << "child is prev_sibling"}; } - if (root->first_child == root->parent) { - throw Error{ERR << "first_child is parent"}; + if (root->child == root->parent) { + throw Error{ERR << "child is parent"}; } } @@ -486,7 +412,7 @@ class PairingHeap final { if (found_nodes.find(root->parent) == std::end(found_nodes)) { throw Error{ERR << "parent node is not known"}; } - element_t child = root->parent->first_child; + element_t child = root->parent->child; element_t lastchild; bool foundvianext = false, foundviaprev = false; @@ -583,13 +509,13 @@ class PairingHeap final { this->walk_tree(this->root_node, func); } -protected: +private: void walk_tree(const element_t &root, const std::function &func) const { func(root); if (root) { - auto node = root->first_child; + auto node = root->child; while (true) { if (not node) { break; @@ -607,18 +533,111 @@ class PairingHeap final { * O(1) */ void push_node(const element_t &node) { - this->root_insert(node); + auto [iter, result] = this->nodes.insert(node); + if (result) + { + this->root_insert(node); + this->node_count += 1; + } -#if OPENAGE_PAIRINGHEAP_DEBUG - auto ins = this->nodes.insert(node); - if (not ins.second) { + #if OPENAGE_PAIRINGHEAP_DEBUG + if (not result) { throw Error{ERR << "node already known"}; } + #endif + } + + + + /** + * returns the smallest item on the heap and deletes it. + * also known as delete_min. + * _________ + * Ω(log log n), O(2^(2*√log log n')) + */ + element_t pop_node() { + if (this->root_node == nullptr) { + throw Error{MSG(err) << "Can't pop an empty heap!"}; + } + + // 0. remove tree root, it's the minimum. + element_t ret = this->root_node; + element_t current_sibling = this->root_node->child; + this->root_node = nullptr; + + // 1. link root children pairwise, last node may be alone + element_t first_pair = nullptr; + element_t previous_pair = nullptr; + + while (current_sibling != nullptr) [[unlikely]] { + element_t link0 = current_sibling; + element_t link1 = current_sibling->next_sibling; + + // pair link0 and link1 + if (link1 != nullptr) { + // get the first sibling for next pair, just in advance. + current_sibling = link1->next_sibling; + + // do the link: merges two nodes, smaller one = root. + element_t link_root = link0->link_with(link1); + link_root->parent = nullptr; + + if (previous_pair == nullptr) { + // this was the first pair + first_pair = link_root; + first_pair->prev_sibling = nullptr; + } + else { + // store node as next sibling in previous pair + previous_pair->next_sibling = link_root; + link_root->prev_sibling = previous_pair; + } + + previous_pair = link_root; + link_root->next_sibling = nullptr; + } + else { + // link0 is the last and unpaired root child. + link0->parent = nullptr; + if (previous_pair == nullptr) { + // link0 was the only node + first_pair = link0; + link0->prev_sibling = nullptr; + } + else { + previous_pair->next_sibling = link0; + link0->prev_sibling = previous_pair; + } + link0->next_sibling = nullptr; + current_sibling = nullptr; + } + } + + + // 2. then link remaining trees to the last one, from right to left + if (first_pair != nullptr) { + this->root_node = first_pair->link_backwards(); + } + + this->node_count -= 1; + size_t erase_result = this->nodes.erase(ret); + + +#if OPENAGE_PAIRINGHEAP_DEBUG + if (1 != erase_result) { + throw Error{ERR << "didn't remove node"}; + } #endif - this->node_count += 1; + // (to find those two lines, 14h of debugging passed) + ret->loosen(); + ret->child = nullptr; + + // and it's done! + return ret; } + /** * insert a node into the heap. */ @@ -631,14 +650,10 @@ class PairingHeap final { } } -protected: compare cmp; size_t node_count; element_t root_node; - -#if OPENAGE_PAIRINGHEAP_DEBUG std::unordered_set nodes; -#endif }; } // namespace openage::datastructure