From da1dfd4c132186e566dd6d451084c70fb3c80be3 Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Mon, 6 May 2024 16:18:11 +0500 Subject: [PATCH] RBTree implementation with const null_node --- src/GraphModel/Attribute.cpp | 4 + src/GraphModel/Edge.cpp | 3 + src/GraphModel/Graph.cpp | 10 +- src/GraphModel/GraphModel.hpp | 7 + src/GraphModel/Node.cpp | 3 + src/RBTree.hpp | 423 ++++++++++++++++++---------------- src/gui/GraphEditor.cpp | 6 +- 7 files changed, 246 insertions(+), 210 deletions(-) diff --git a/src/GraphModel/Attribute.cpp b/src/GraphModel/Attribute.cpp index 5ffb1e3..e3527da 100644 --- a/src/GraphModel/Attribute.cpp +++ b/src/GraphModel/Attribute.cpp @@ -2,10 +2,14 @@ namespace GraphC::GraphModel { + Attribute::Attribute(id_t _id, Node* _parent_node, Attribute::Type _type, std::string _title) : type(_type), id(_id), parent_node(_parent_node), title(_title) {} +Attribute::Attribute() : Attribute(id_t_invalid, nullptr, Attribute::Type::Static, "NULL_ATTR") +{} + const RBTree& Attribute::getIncomingEdges() const { return incoming_edges; } diff --git a/src/GraphModel/Edge.cpp b/src/GraphModel/Edge.cpp index 59988c5..fc863b7 100644 --- a/src/GraphModel/Edge.cpp +++ b/src/GraphModel/Edge.cpp @@ -6,4 +6,7 @@ Edge::Edge(id_t _id, id_t _from_attr_id, id_t _to_attr_id) : id(_id), to_attr_id(_to_attr_id), from_attr_id(_from_attr_id) {} +Edge::Edge() : Edge(id_t_invalid, id_t_invalid, id_t_invalid) +{} + } diff --git a/src/GraphModel/Graph.cpp b/src/GraphModel/Graph.cpp index b379fc8..fb31dc2 100644 --- a/src/GraphModel/Graph.cpp +++ b/src/GraphModel/Graph.cpp @@ -32,7 +32,7 @@ bool Graph::tryGetNode(id_t node_id, Node** result) const { void Graph::deleteNode(Node& n){ for(const auto& attr_p : n.attributes) - deleteAttribute(*attr_p.second); + deleteAttribute(*attr_p.value); useful_assert(nodes.tryDelete(n.id), format("can't delete node with id %i", n.id)); } @@ -141,12 +141,12 @@ Attribute* Graph::createAttribute(Attribute&& a){ void Graph::deleteAttribute(Attribute& a){ for(const auto& edge_p : a.incoming_edges){ - useful_assert(edges.tryDelete(edge_p.second->id), - format("can't find edge with id %i", edge_p.second->id)); + useful_assert(edges.tryDelete(edge_p.value->id), + format("can't find edge with id %i", edge_p.value->id)); } for(const auto& edge_p : a.outgoing_edges){ - useful_assert(edges.tryDelete(edge_p.second->id), - format("can't find edge with id %i", edge_p.second->id)); + useful_assert(edges.tryDelete(edge_p.value->id), + format("can't find edge with id %i", edge_p.value->id)); } useful_assert(attributes.tryDelete(a.id), format("can't delete attribute with id %i", a.id)); diff --git a/src/GraphModel/GraphModel.hpp b/src/GraphModel/GraphModel.hpp index b71432a..72aee6e 100644 --- a/src/GraphModel/GraphModel.hpp +++ b/src/GraphModel/GraphModel.hpp @@ -9,6 +9,7 @@ namespace GraphC::GraphModel { typedef i32 id_t; +#define id_t_invalid ((id_t)-1) class IdGenerator { id_t next_id=1; @@ -39,6 +40,8 @@ public: Node *const parent_node; std::string title; + ///@warning empty constructor for RBTree null-value node, do not use it + Attribute(); Attribute(id_t _id, Node* _parent_node, Attribute::Type _type, std::string _title); const RBTree& getIncomingEdges() const; @@ -54,6 +57,8 @@ public: const id_t id; std::string title; + ///@warning empty constructor for RBTree null-value node, do not use it + Node(); Node(id_t _id, std::string _title); const RBTree& getAttributes() const; @@ -64,6 +69,8 @@ struct Edge { const id_t to_attr_id; const id_t from_attr_id; + ///@warning empty constructor for RBTree null-value node, do not use it + Edge(); Edge(id_t _id, id_t _from_attr_id, id_t _to_attr_id); }; diff --git a/src/GraphModel/Node.cpp b/src/GraphModel/Node.cpp index b72eba5..dc9bc67 100644 --- a/src/GraphModel/Node.cpp +++ b/src/GraphModel/Node.cpp @@ -5,6 +5,9 @@ namespace GraphC::GraphModel { Node::Node(id_t _id, std::string _title) : id(_id), title(_title) {} + +Node::Node() : Node(id_t_invalid, "NULL_NODE") +{} const RBTree& Node::getAttributes() const { return attributes; diff --git a/src/RBTree.hpp b/src/RBTree.hpp index dbff5ea..40dc6ca 100644 --- a/src/RBTree.hpp +++ b/src/RBTree.hpp @@ -1,80 +1,87 @@ #pragma once -#include "../../dependencies/kerep/src/base/base.h" -#include #include +#include "UsefulException.hpp" -// typedef char* TKey; -// typedef char* TVal; template class RBTree { enum class Color { Red, Black }; - struct Node { - TKey key; + // public version of struct Node + // TreeIterator returns a reference to KVPair to hide all other stuff from users +public: + struct KVPair { + // members are in reverse order to minimize padding and make struct Node smaller + TVal value; + TKey key; + + KVPair(TKey& k, TVal& v) : value(v), key(k) + {} + + protected: + // special constructor for tree leafs (null nodes) + // leaves key and value uninitialized + KVPair() + {} + }; + +private: + struct Node : public KVPair { + // stacks with KVPair::key if it is 32-bit or smaller Color color; Node* parent; - Node* left = nullptr; - Node* right = nullptr; + Node* left; + Node* right; - Node(TKey _key, TVal _val, Color _color, Node* _parent) - : key(_key), value(_val), color(_color), parent(_parent) - { + Node(TKey& _key, TVal& _val, Color _color, Node* _parent, Node* _left, Node* _right) + : KVPair(_key, _val), color(_color), parent(_parent), left(_left), right(_right) + {} + + Node() : color(Color::Black), parent(this), left(this), right(this) + {} + + bool isLeaf() const { + return parent == this; } ~Node(){ - delete left; - delete right; + if(!left->isLeaf()) + delete left; + if(!right->isLeaf()) + delete right; } - inline Node* getSibling(){ - if(parent == nullptr) - return nullptr; - else if(parent->left == this) - return parent->right; - else return parent->left; - } - - inline Node* getGrandparent(){ - if(parent == nullptr) - return nullptr; - else return parent->parent; - } - - inline Node* getUncle(){ - if(parent == nullptr) - return nullptr; - return parent->getSibling(); - } - - // n should be not null - Node* getMinChild(){ + /// if is leaf returns itself + Node* getMinChild() { Node* n = this; - while(n->left != nullptr) + while(!n->left->isLeaf()) n = n->left; return n; } - // n should be not null - Node* getMaxChild(){ + /// if is leaf returns itself + Node* getMaxChild() { Node* n = this; - while(n->right != nullptr) + while(!n->right->isLeaf()) n = n->right; return n; } }; - Node* root = nullptr; + static Node null_node; + static Node* null_node_ptr; + Node* root = &null_node; + ///@returns null if root is null Node* findParentForKey(TKey key) const { Node* n = root; - Node* parent = nullptr; - while(n != nullptr){ + Node* parent = null_node_ptr; + while(n != null_node_ptr){ parent = n; if(key < n->key) n = n->left; @@ -90,7 +97,7 @@ class RBTree { Node* y = x->right; // 2. move y to the position of x y->parent = x->parent; - if (x->parent != nullptr){ // x != root + if(x->parent != null_node_ptr){ // x != root if(x == x->parent->left) x->parent->left = y; else x->parent->right = y; @@ -98,7 +105,7 @@ class RBTree { else root = y; // 3. move y.left to x.right if it exists x->right = y->left; - if (x->right != nullptr) + if(x->right != null_node_ptr) x->right->parent = x; // 4. move x to y.left y->left = x; @@ -110,7 +117,7 @@ class RBTree { Node* y = x->left; // 2. move y up y->parent = x->parent; - if (x->parent != nullptr){ // x != root + if(x->parent != null_node_ptr){ // x != root if(x == x->parent->left) x->parent->left = y; else x->parent->right = y; @@ -118,7 +125,7 @@ class RBTree { else root = y; // 3. move y.right to x.left if it exists x->left = y->right; - if (x->left != nullptr) + if(x->left != null_node_ptr) x->left->parent = x; // 4. move x to y.right y->right = x; @@ -126,128 +133,124 @@ class RBTree { } void transplantNode(Node* old, Node* neww){ - if(old->parent == nullptr) + if(old->parent == null_node_ptr) root = neww; else if(old->parent->left == old) old->parent->left = neww; else old->parent->right = neww; - if(neww != nullptr) + if(neww != null_node_ptr) neww->parent = old->parent; } - void fixupInsertion(Node* n){ - // case 1: n is root -- root must be black - if (n->parent == nullptr){ - n->color = Color::Black; - return; - } - - // case 2: parent is black -- no requirements mismatch - if (n->parent->color == Color::Black) - return; - - // case 3: parent and uncle are red -- red nodes must have black parents - Node* u = n->getUncle(); - Node* g = n->getGrandparent(); - if(u != nullptr && u->color == Color::Red){ - n->parent->color = Color::Black; - u->color = Color::Black; - g->color = Color::Red; - fixupInsertion(g); - return; - } - - // case 4: parent is red and uncle is black -- red nodes must have black parents - if ((n == n->parent->right) && (n->parent == g->left)) { - rotateLeft(n->parent); - n = n->left; - } - else if ((n == n->parent->left) && (n->parent == g->right)) { - rotateRight(n->parent); - n = n->right; - } - - // case 5 - n->parent->color = Color::Black; - g->color = Color::Red; - if ((n == n->parent->left) && (n->parent == g->left)) - rotateRight(g); - else rotateLeft(g); - } - - void fixupDeletion(Node* n){ - // case 1 - if(n->parent == nullptr) - return; - - // case 2 - Node* s = n->getSibling(); - if(s->color == Color::Red){ - n->parent->color = Color::Red; - s->color = Color::Black; - if (n == n->parent->left) - rotateLeft(n->parent); - else rotateRight(n->parent); - } - - // case 3 - if ((n->parent->color == Color::Black) && - (s->color == Color::Black) && - (s->left->color == Color::Black) && - (s->right->color == Color::Black)) - { - s->color = Color::Red; - fixupDeletion(n->parent); - return; - } - - // case 4 - else if ((n->parent->color == Color::Red) && - (s->color == Color::Black) && - (s->left->color == Color::Black) && - (s->right->color == Color::Black)) - { - s->color = Color::Red; - n->parent->color = Color::Black; - return; - } - - // case 5 - if(s->color == Color::Black) { - if ((n == n->parent->left) && - (s->right->color == Color::Black) && - (s->left->color == Color::Red)) - { - s->color = Color::Red; - s->left->color = Color::Black; - rotateRight(s); - } - else if ((n == n->parent->right) && - (s->left->color == Color::Black) && - (s->right->color == Color::Red)) - { - s->color = Color::Red; - s->right->color = Color::Black; - rotateLeft(s); + void fixupInsertion(Node* x){ + while(x != root && x->parent->color == Color::Red) { + if(x->parent == x->parent->parent->left) { + Node* y = x->parent->parent->right; + // uncle is red + if(y->color == Color::Red) { + x->parent->color = Color::Black; + y->color = Color::Black; + x->parent->parent->color = Color::Red; + x = x->parent->parent; + } + // uncle is black + else { + if(x == x->parent->right) { + // make x a left child + x = x->parent; + rotateLeft(x); + } + // recolor and rotate + x->parent->color = Color::Black; + x->parent->parent->color = Color::Red; + rotateRight(x->parent->parent); + } + } + // mirrored above code + else { + Node* y = x->parent->parent->left; + // uncle is red + if(y->color == Color::Red) { + x->parent->color = Color::Black; + y->color = Color::Black; + x->parent->parent->color = Color::Red; + x = x->parent->parent; + } + // uncle is black + else { + if(x == x->parent->left) { + // make x a right child + x = x->parent; + rotateRight(x); + } + // recolor and rotate + x->parent->color = Color::Black; + x->parent->parent->color = Color::Red; + rotateLeft(x->parent->parent); + } } } - - // case 6 - s->color = n->parent->color; - n->parent->color = Color::Black; - - if (n == n->parent->left) { - s->right->color = Color::Black; - rotateLeft(n->parent); - } else { - s->left->color = Color::Black; - rotateRight(n->parent); - } + root->color = Color::Black; } + void fixupDeletion(Node* x){ + while(x != root && x->color == Color::Black) { + if(x == x->parent->left) { + Node* w = x->parent->right; + if(w->color == Color::Red) { + w->color = Color::Black; + x->parent->color = Color::Red; + rotateLeft(x->parent); + w = x->parent->right; + } + if(w->left->color == Color::Black && w->right->color == Color::Black) { + w->color = Color::Red; + x = x->parent; + } else { + if(w->right->color == Color::Black) { + w->left->color = Color::Black; + w->color = Color::Red; + rotateRight(w); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = Color::Black; + w->right->color = Color::Black; + rotateLeft(x->parent); + x = root; + } + } + else { + Node* w = x->parent->left; + if(w->color == Color::Red) { + w->color = Color::Black; + x->parent->color = Color::Red; + rotateRight(x->parent); + w = x->parent->left; + } + if(w->right->color == Color::Black && w->left->color == Color::Black) { + w->color = Color::Red; + x = x->parent; + } else { + if(w->left->color == Color::Black) { + w->right->color = Color::Black; + w->color = Color::Red; + rotateLeft(w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = Color::Black; + w->left->color = Color::Black; + rotateRight(x->parent); + x = root; + } + } + } + x->color = Color::Black; + } - template - struct TreeIterator : std::iterator { + template + struct TreeIterator { Node* n; TreeIterator(TreeIterator const& src){ @@ -266,20 +269,20 @@ class RBTree { return n == other.n; } - TIteratorValue& operator*() const { - if(n == nullptr) - throw "RBTree::TreeIterator::operator*() error: n == nullptr"; - return *((TIteratorValue*)(void*)n); + TreeIteratorValue& operator*() const { + if(!n->isLeaf()) + return *n; + else throw UsefulException("the caller has tried to get the value of the end of an iterator (null node value)"); } void operator++() { - if(n == nullptr) + if(n->isLeaf()) return; - if(n->right) + if(!n->right->isLeaf()) n = n->right->getMinChild(); else { Node* p = n->parent; - while(p != nullptr && n == p->right){ + while(n == p->right){ n = p; p = p->parent; } @@ -289,20 +292,21 @@ class RBTree { }; public: - using iterator = TreeIterator>; - using const_iterator = TreeIterator>; + using iterator = TreeIterator; + using const_iterator = TreeIterator; RBTree() {} ~RBTree(){ - delete root; + if(!root->isLeaf()) + delete root; } /// @param resultPtr nullable bool tryAdd(TKey key, TVal& value, TVal** resultPtr){ - if(root == nullptr){ - root = new Node(key, value, Color::Black, nullptr); + if(root == null_node_ptr){ + root = new Node(key, value, Color::Black, null_node_ptr, null_node_ptr, null_node_ptr); if(resultPtr) *resultPtr = &root->value; return true; @@ -316,14 +320,14 @@ public: else nodePtrPtr = &parent->right; // if a child node already exists at this place, returns false - if(*nodePtrPtr != nullptr){ + if(*nodePtrPtr != null_node_ptr){ if(resultPtr) *resultPtr = nullptr; return false; } // places newNode to left or right of the parent - Node* newNode = new Node(key, value, Color::Red, parent); + Node* newNode = new Node(key, value, Color::Red, parent, null_node_ptr, null_node_ptr); if(resultPtr) *resultPtr = &newNode->value; *nodePtrPtr = newNode; @@ -340,7 +344,7 @@ public: /// @param resultPtr nullable bool trySet(TKey key, TVal& value, TVal** resultPtr){ - if(root == nullptr){ + if(root == null_node_ptr){ if(resultPtr) *resultPtr = nullptr; return false; @@ -354,14 +358,14 @@ public: else nodePtrPtr = &parent->right; // if a child node with the given key doesn't exist, returns false - if(*nodePtrPtr == nullptr){ + if(*nodePtrPtr == null_node_ptr){ if(resultPtr) *resultPtr = nullptr; return false; } // replaces the value of left or right child of the parent - (*nodePtrPtr)->value = value; + (*nodePtrPtr)->value = value; if(resultPtr) *resultPtr = &(*nodePtrPtr)->value; return true; @@ -375,8 +379,8 @@ public: /// @param resultPtr nullable void addOrSet(TKey key, TVal& value, TVal** resultPtr){ - if(root == nullptr){ - root = new Node(key, value, Color::Black, nullptr); + if(root == null_node_ptr){ + root = new Node(key, value, Color::Black, null_node_ptr, null_node_ptr, null_node_ptr); if(resultPtr != nullptr) *resultPtr = &root->value; return; @@ -390,15 +394,15 @@ public: else nodePtrPtr = &parent->right; // if a child node already exists at this place, sets it's value - if(*nodePtrPtr != nullptr){ - (*nodePtrPtr)->value = value; + if(*nodePtrPtr != null_node_ptr){ + (*nodePtrPtr)->value = value; if(resultPtr) *resultPtr = &(*nodePtrPtr)->value; return; } // places newNode to left or right of the parent - Node* newNode = new Node(key, value, Color::Red, parent); + Node* newNode = new Node(key, value, Color::Red, parent, null_node_ptr, null_node_ptr); if(resultPtr != nullptr) *resultPtr = &newNode->value; *nodePtrPtr = newNode; @@ -416,14 +420,14 @@ public: if(!resultPtr) return false; Node* parent = findParentForKey(key); - Node* n = nullptr; - if(parent == nullptr) + Node* n = null_node_ptr; + if(parent == null_node_ptr) n = root; else if(key < parent->key) n = parent->left; else n = parent->right; // if there is no node with the given key - if(n == nullptr){ + if(n == null_node_ptr){ *resultPtr = nullptr; return false; } @@ -435,25 +439,25 @@ public: bool tryDelete(TKey key){ Node* parent = findParentForKey(key); - Node* n = nullptr; - if(parent == nullptr) + Node* n = null_node_ptr; + if(parent == null_node_ptr) n = root; else if(key < parent->key) n = parent->left; else n = parent->right; // key not found - if(n == nullptr){ + if(n == null_node_ptr){ return false; } - if(n->left == nullptr){ + if(n->left == null_node_ptr){ transplantNode(n, n->right); - if(n->color == Color::Black && n->right != nullptr) + if(n->color == Color::Black && n->right != null_node_ptr) fixupDeletion(n->right); } - else if(n->right == nullptr){ + else if(n->right == null_node_ptr){ transplantNode(n, n->left); - if(n->color == Color::Black && n->left != nullptr) + if(n->color == Color::Black && n->left != null_node_ptr) fixupDeletion(n->left); } else { @@ -471,55 +475,63 @@ public: fixupDeletion(minNode->right); } + // delete node without children + n->left = null_node_ptr; + n->right = null_node_ptr; delete n; return true; } iterator begin(){ - if(root == nullptr) - return iterator(nullptr); + if(root == null_node_ptr) + return iterator(null_node_ptr); return iterator(root->getMinChild()); } iterator end(){ - if(root == nullptr) - return iterator(nullptr); - return iterator(root->getMaxChild()); + return iterator(null_node_ptr); } const_iterator begin() const { - if(root == nullptr) - return const_iterator(nullptr); + if(root == null_node_ptr) + return const_iterator(null_node_ptr); return const_iterator(root->getMinChild()); } const_iterator end() const { - return const_iterator(nullptr); + return const_iterator(null_node_ptr); } void _generateGraphVizCodeForChildren(std::stringstream& ss, Node* n) const { - if(!n) + if(n == null_node_ptr) return; + if(n->color == Color::Red) ss<<" \""<key<<"\" [color=red]\n"; - if(n->left){ + + if(n->left == null_node_ptr){ + ss<<" \""<key<<"\" -> \"null\" [side=L]\n"; + } + else { ss<<" \""<key<<"\" -> \""<left->key<<"\" [side=L]\n"; _generateGraphVizCodeForChildren(ss, n->left); } - else ss<<" \""<key<<"\" -> \"null\" [side=L]\n"; - if(n->right){ + + if(n->right == null_node_ptr){ + ss<<" \""<key<<"\" -> \"null\" [side=R]\n"; + } + else { ss<<" \""<key<<"\" -> \""<right->key<<"\" [side=R]\n"; _generateGraphVizCodeForChildren(ss, n->right); } - else ss<<" \""<key<<"\" -> \"null\" [side=R]\n"; } std::string generateGraphVizCode() const { std::stringstream ss; ss<<"digraph {\n" " node [style=filled,color=gray];\n"; - if(root == nullptr) + if(root == null_node_ptr) ss<<" \"null\"\n"; else { ss<<" \"null-parent\" -> \""<key<<"\"\n"; @@ -529,3 +541,10 @@ public: return ss.str(); } }; + + +template +typename RBTree::Node RBTree::null_node; + +template +typename RBTree::Node* RBTree::null_node_ptr = &RBTree::null_node; diff --git a/src/gui/GraphEditor.cpp b/src/gui/GraphEditor.cpp index abe8a97..ad89e25 100644 --- a/src/gui/GraphEditor.cpp +++ b/src/gui/GraphEditor.cpp @@ -11,7 +11,7 @@ void GraphEditor::drawNode(const GraphModel::Node& node){ ImNodes::EndNodeTitleBar(); for(auto& a_p : node.getAttributes()){ - auto&& a = *a_p.second; + auto&& a = *a_p.value; switch (a.type){ case GraphModel::Attribute::Type::Input: ImNodes::BeginInputAttribute(a.id); @@ -140,11 +140,11 @@ void GraphEditor::draw(){ ImNodes::BeginNodeEditor(); // draw nodes for(auto& n_p : graph.getNodes()){ - drawNode(n_p.second); + drawNode(n_p.value); } // draw edges for(auto& p : graph.getEdges()){ - ImNodes::Link(p.second.id, p.second.to_attr_id, p.second.from_attr_id); + ImNodes::Link(p.value.id, p.value.to_attr_id, p.value.from_attr_id); } ImNodes::EndNodeEditor();