// =====================================================================================================================
// open-cpp-utils, an open-source cpp library with data structures that extend the STL.
// Copyright (C) 2024 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
// =====================================================================================================================
#ifndef OPEN_CPP_UTILS_DIRECTED_TREE_H
#define OPEN_CPP_UTILS_DIRECTED_TREE_H
#include
#include
#include
#include
namespace open_cpp_utils
{
/**
* \brief Class for creating a directed tree
* \tparam T Type of the data associated with each node
*
* The tree is a series of child nodes in forward linked lists.
*
*/
template>
class directed_tree
{
// Forward Definitions =================================================================================================
public:
class breadth_first;
class pre_order;
class in_order;
class post_order;
class unordered;
private:
struct Node_;
// Typedefs ============================================================================================================
public:
using data_type = T;
using node = size_t;
using node_queue = std::deque;
private:
using s_alloc = Alloc;
using h_alloc = typename std::allocator_traits::template rebind_alloc; // Gross
using hierarchy = Node_*;
using storage = data_type*;
// Constants ===========================================================================================================
public:
static constexpr std::integral_constant root{};
// Data Structures =====================================================================================================
private:
struct Node_
{
enum flags
{
valid = 0x0001
};
node parent, child, prev_sibling, next_sibling;
uint32_t flags, depth;
Node_() : parent(0), child(0), prev_sibling(0), next_sibling(0), flags(0), depth(0) { }
};
// Functions ===========================================================================================================
private:
// Helpers -------------------------------------------------------------------------------------------------------------
void grow_()
{
hierarchy g_old = graph_;
storage d_old = data_;
size_t c_old = capacity_;
if(capacity_ == 0) capacity_ = 10;
else capacity_ *= 2;
graph_ = g_alloc_.allocate(capacity_);
data_ = d_alloc_.allocate(capacity_);
if(size_ > 0)
{
std::memcpy(graph_, g_old, size_ * sizeof(Node_));
std::memcpy(data_, d_old, size_ * sizeof(data_type));
for(node i = size_; i < capacity_; ++i)
{
graph_[i] = Node_();
std::construct_at(data_ + i);
}
}
else
{
for(node i = 0; i < capacity_; ++i)
{
graph_[i] = Node_();
std::construct_at(data_ + i);
}
graph_[0].flags = Node_::valid;
}
g_alloc_.deallocate(g_old, c_old);
d_alloc_.deallocate(d_old, c_old);
}
node push_back_(const data_type& data)
{
if(size_ >= capacity_) grow_();
std::construct_at(data_ + size_, data);
return size_++;
}
node push_back_(data_type&& data)
{
if(size_ >= capacity_) grow_();
std::construct_at(data_ + size_, std::forward(data));
return size_++;
}
public:
// Constructors & Destructor -------------------------------------------------------------------------------------------
/**
* \brief Default constructor, creates tree with empty root
*/
directed_tree()
: size_(0), capacity_(0)
, graph_(nullptr), data_(nullptr)
{ push_back_(T()); }
directed_tree(data_type&& data)
: size_(0), capacity_(0)
, graph_(nullptr), data_(nullptr)
{ push_back_(std::forward(data)); }
directed_tree(const data_type& data)
: size_(0), capacity_(0)
, graph_(nullptr), data_(nullptr)
{ push_back_(data); }
~directed_tree() = default;
// Tree Navigation -----------------------------------------------------------------------------------------------------
/**
* \brief Check whether a node is valid. O(1)
* \param id Node id to reference
* \return Whether the valid flag is true in the node
*/
[[nodiscard]] bool valid(node id) const { return graph_ ? graph_[id].flags & Node_::valid : false; }
/**
* \brief Get the parent of a node. O(1)
* \param id Node id to reference
* \return Node id of the parent
*/
[[nodiscard]] node parent(node id) const { return graph_ ? graph_[id].parent : 0; }
/**
* \brief Get the first child of a node. O(1)
* \param id Node id to reference
* \return Node id of the first child
*/
[[nodiscard]] node first_child(node id) const { return graph_ ? graph_[id].child : 0; }
/**
* \brief Get the first child of a node. O(1)
* \param id Node id to reference
* \return Node id of the first child
*/
[[nodiscard]] node last_child(node id) const
{
node c = first_child(id);
while(c != 0) { if(graph_[c].next_sibling == 0) break; c = graph_[c].next_sibling; }
return c;
}
/**
* \brief Get the previous sibling of a node. O(1)
* \param id Node id to reference
* \return Node id of the next sibling in the linked list
*/
[[nodiscard]] node prev_sibling(node id) const { return graph_[id].prev_sibling; }
/**
* \brief Get the next sibling of a node. O(1)
* \param id Node id to reference
* \return Node id of the next sibling in the linked list
*/
[[nodiscard]] node next_sibling(node id) const { return graph_[id].next_sibling; }
/**
* \brief Get the left most child of a node. O(log(n))
* \param id Node id to reference
* \return Node id of the left most child
*/
[[nodiscard]] node left_most(node id) const
{
node current = id;
while(id = first_child(current)) current = id;
return current;
}
/**
* \brief Get the depth of a node
* \param id
* \return
*/
[[nodiscard]] uint32_t depth(node id) const { return graph_[id].depth; }
// Tree Modification ---------------------------------------------------------------------------------------------------
/**
* \brief Get the next id that would be used if insert() were called
* \return Next node id
*/
node next_id() const
{
if(freed_.empty()) return static_cast(size_);
return freed_.front();
}
/**
* \brief Insert a node into the tree as a child of the provided node
* \param data Value to insert
* \param p_id Id of the parent node
* \param sib Child to insert before, passing root specifies to insert to the back
* \return Id of the inserted node
*/
node insert(const data_type& data, node p_id, node sib = 0)
{
// If there are no freed nodes, create a new node and mark it as freed
if(freed_.empty())
{
freed_.push_back(push_back_(std::forward(data)));
}
else
{
std::construct_at(data_ + freed_.front(), std::forward(data));
}
// Pop a freed node from the stack
node id = freed_.front(); freed_.pop_front();
bool back = sib == 0;
node s_id = back ? last_child(p_id) : sib;
Node_& node = graph_[id];
Node_& parent = graph_[p_id];
Node_& sibling = graph_[s_id];
if(parent.child == root || (s_id == parent.child && !back)) parent.child = id;
node.next_sibling = node.prev_sibling = 0;
node.parent = p_id;
node.depth = parent.depth + 1;
node.flags = Node_::valid;
node.child = 0;
if(s_id == 0) return id;
if(back)
{
node.next_sibling = sibling.next_sibling;
node.prev_sibling = s_id;
sibling.next_sibling = id;
}
else
{
node.next_sibling = s_id;
node.prev_sibling = sibling.prev_sibling;
sibling.prev_sibling = id;
}
return id;
}
/**
* \brief Insert a node into the tree as a child of the provided node
* \param data Value to insert
* \param p_id Id of the parent node
* \param sib Child to insert before, passing root specifies to insert to the back
* \return Id of the inserted node
*/
node insert(data_type&& data, node p_id, node sib = root)
{
// If there are no freed nodes, create a new node and mark it as freed
if(freed_.empty())
{
freed_.push_back(push_back_(std::forward(data)));
}
else
{
std::construct_at(data_ + freed_.front(), std::forward(data));
}
// Pop a freed node from the stack
node id = freed_.front(); freed_.pop_front();
bool back = sib == root;
node s_id = back ? last_child(p_id) : sib;
Node_& node = graph_[id];
Node_& parent = graph_[p_id];
Node_& sibling = graph_[s_id];
if(parent.child == root || (s_id == parent.child && !back)) parent.child = id;
node.next_sibling = node.prev_sibling = 0;
node.parent = p_id;
node.depth = parent.depth + 1;
node.flags = Node_::valid;
node.child = 0;
if(s_id == 0) return id;
if(back)
{
node.next_sibling = sibling.next_sibling;
node.prev_sibling = s_id;
sibling.next_sibling = id;
}
else
{
node.next_sibling = s_id;
node.prev_sibling = sibling.prev_sibling;
sibling.prev_sibling = id;
}
return id;
}
void swap(node a, node b)
{
Node_& A = graph_[a];
Node_& B = graph_[b];
std::swap(A, B);
if(graph_[B.parent].child == a) graph_[B.parent].child = b;
if(graph_[A.parent].child == b) graph_[A.parent].child = a;
}
void clear()
{
for(int i = 0; i < size_; ++i)
{
if(valid(i) == false) continue;
graph_[i].flags = 0;
std::destroy_at(data_ + i);
freed_.push_back(i);
}
g_alloc_.deallocate(graph_, capacity_);
d_alloc_.deallocate(data_, capacity_);
capacity_ = 0; size_ = 0;
}
/**
* \brief Erase a node in the tree. O(n)
* \param id Id of the node to erase
*/
void erase(node id)
{
if(id == root) return;
// Mark node as invalid and push it to the freed list
Node_& erased = graph_[id];
erased.flags = 0;
freed_.push_back(id);
std::destroy_at(data_ + id);
// Update the parent's child
graph_[erased.parent].child = erased.next_sibling;
// Update siblings
if(erased.next_sibling) graph_[erased.next_sibling].prev_sibling = erased.prev_sibling;
if(erased.prev_sibling) graph_[erased.prev_sibling].next_sibling = erased.next_sibling;
// Erase children - essentially breadth first propagation down the tree
node_queue stack{ erased.child };
while(stack.empty() == false)
{
node next = stack.front(); stack.pop_front();
Node_& child = graph_[next];
child.flags = 0;
freed_.push_back(next);
std::destroy_at(data_ + next);
if(child.next_sibling) stack.push_front(child.next_sibling);
if(child.child) stack.push_front(child.child);
}
}
// Tree Access ---------------------------------------------------------------------------------------------------------
/**
* \brief Getter for data associated with a node
* \param id Id of the node to access
* \return Reference to the node's data
*/
data_type& operator[](node id) { return data_[id]; }
/**
* \brief Constant getter for data associated with a node
* \param node Id of the node to access
* \return Reference to the node's data
*/
[[nodiscard]] const data_type& operator[](node id) const { return data_[id]; }
// Visitor Pattern -----------------------------------------------------------------------------------------------------
/**
* \brief Traverser-Visitor pattern for accessing the tree
* \tparam V Visitor type.
* \tparam O Order type. Defaults to Pre-Order Traversal.
* \param visitor
*/
template
void traverse(V& visitor)
{
traverser traverser(*this, visitor);
traverser();
}
// Variables =======================================================================================================
private:
h_alloc g_alloc_;
s_alloc d_alloc_;
size_t size_, capacity_;
hierarchy graph_;
storage data_;
node_queue freed_;
// Navigation ======================================================================================================
public:
class unordered
{
public:
unordered(directed_tree& graph) : graph_(graph), current_(root) { }
node operator()(node id)
{
while(!graph_.valid(current_) || current_ == root)
{
++current_;
}
id = current_;
current_ ++;
return id == graph_.graph_.size() ? 0 : id;
}
private:
directed_tree& graph_;
node current_;
};
/**
* \brief Breadth first traversal
*/
class breadth_first
{
public:
breadth_first(directed_tree& graph) : graph_(graph), visit_queue_(0) { }
node operator()(node id)
{
id = visit_queue_.back(); visit_queue_.pop_back();
Node_& current = graph_.graph_[id];
if(current.next_sibling) visit_queue_.push_back(current.next_sibling);
if(current.child) visit_queue_.push_front(current.child);
if(visit_queue_.empty()) return 0;
return id;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
/**
* \brief Pre-order traversal
*/
class pre_order
{
public:
pre_order(directed_tree& graph) : graph_(graph) { }
node operator()(node id)
{
Node_& current = graph_.graph_[id];
if(current.next_sibling) visit_queue_.push_front(current.next_sibling);
if(current.child) visit_queue_.push_front(current.child);
if(visit_queue_.empty()) return 0;
node next = visit_queue_.front(); visit_queue_.pop_front();
return next;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
/**
* \brief In-order traversal
*/
class in_order
{
public:
in_order(directed_tree& graph) : graph_(graph) { }
node operator()(node id)
{
if(id == 0) visit_queue_.push_back(graph_.left_most(id));
id = visit_queue_.front(); visit_queue_.pop_front();
Node_& current = graph_.graph_[id];
if(current.Sibling)
{
if(graph_.next_sibling(current.Sibling)) visit_queue_.push_back(current.parent);
visit_queue_.push_back(graph_.left_most(current.Sibling));
}
return id;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
/**
* \brief Post-order traversal
*/
class post_order
{
public:
post_order(directed_tree& graph) : graph_(graph) { }
node operator()(node id)
{
if(visit_queue_.empty()) visit_queue_.push_back(graph_.left_most(id));
id = visit_queue_.front(); visit_queue_.pop_front();
if(id == 0) return id;
Node_& current = graph_.graph_[id];
visit_queue_.push_back(current.Sibling ? graph_.left_most(current.Sibling) : graph_.parent(id));
return id;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
/**
* \brief Visitor pattern for traversing the tree
*/
template
class traverser
{
public:
using visitor_type = V;
using order_type = O;
traverser(directed_tree& graph, visitor_type& visitor) : graph_(graph), visitor_(visitor), order_(graph) { }
void operator()()
{
node id = 0;
while(id = order_(id))
{
if(visitor_(graph_[id], id)) break;
}
}
private:
directed_tree& graph_;
visitor_type& visitor_;
order_type order_;
};
};
}
#endif // OPEN_CPP_UTILS_DIRECTED_TREE_H