RBTree implementation with const null_node

This commit is contained in:
Timerix22 2024-05-06 16:18:11 +05:00
parent 2050eb4d46
commit da1dfd4c13
7 changed files with 246 additions and 210 deletions

View File

@ -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<id_t, Edge*>& Attribute::getIncomingEdges() const {
return incoming_edges;
}

View File

@ -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)
{}
}

View File

@ -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));

View File

@ -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<id_t, Edge*>& 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<id_t, Attribute*>& 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);
};

View File

@ -6,6 +6,9 @@ Node::Node(id_t _id, std::string _title)
: id(_id), title(_title)
{}
Node::Node() : Node(id_t_invalid, "NULL_NODE")
{}
const RBTree<id_t, Attribute*>& Node::getAttributes() const {
return attributes;
}

View File

@ -1,80 +1,87 @@
#pragma once
#include "../../dependencies/kerep/src/base/base.h"
#include <map>
#include <sstream>
#include "UsefulException.hpp"
// typedef char* TKey;
// typedef char* TVal;
template<typename TKey, typename TVal>
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(){
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
/// 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
/// 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;
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;
}
// 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;
// uncle is black
else {
if(x == x->parent->right) {
// make x a left child
x = x->parent;
rotateLeft(x);
}
// 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);
// 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);
}
}
}
root->color = Color::Black;
}
// 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);
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 {
s->left->color = Color::Black;
rotateRight(n->parent);
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<typename TIteratorValue>
struct TreeIterator : std::iterator<std::bidirectional_iterator_tag, TIteratorValue> {
template<typename TreeIteratorValue>
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<std::pair<const TKey, TVal>>;
using const_iterator = TreeIterator<std::pair<const TKey, const TVal>>;
using iterator = TreeIterator<KVPair>;
using const_iterator = TreeIterator<const KVPair>;
RBTree() {}
~RBTree(){
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,7 +358,7 @@ 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;
@ -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,7 +394,7 @@ public:
else nodePtrPtr = &parent->right;
// if a child node already exists at this place, sets it's value
if(*nodePtrPtr != nullptr){
if(*nodePtrPtr != null_node_ptr){
(*nodePtrPtr)->value = value;
if(resultPtr)
*resultPtr = &(*nodePtrPtr)->value;
@ -398,7 +402,7 @@ public:
}
// 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<<" \""<<n->key<<"\" [color=red]\n";
if(n->left){
if(n->left == null_node_ptr){
ss<<" \""<<n->key<<"\" -> \"null\" [side=L]\n";
}
else {
ss<<" \""<<n->key<<"\" -> \""<<n->left->key<<"\" [side=L]\n";
_generateGraphVizCodeForChildren(ss, n->left);
}
else ss<<" \""<<n->key<<"\" -> \"null\" [side=L]\n";
if(n->right){
if(n->right == null_node_ptr){
ss<<" \""<<n->key<<"\" -> \"null\" [side=R]\n";
}
else {
ss<<" \""<<n->key<<"\" -> \""<<n->right->key<<"\" [side=R]\n";
_generateGraphVizCodeForChildren(ss, n->right);
}
else ss<<" \""<<n->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\" -> \""<<root->key<<"\"\n";
@ -529,3 +541,10 @@ public:
return ss.str();
}
};
template<typename TKey, typename TVal>
typename RBTree<TKey, TVal>::Node RBTree<TKey, TVal>::null_node;
template<typename TKey, typename TVal>
typename RBTree<TKey, TVal>::Node* RBTree<TKey, TVal>::null_node_ptr = &RBTree<TKey, TVal>::null_node;

View File

@ -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();