# open-cpp-utils: directed_tree.h Source File

Loading...
Searching...
No Matches
directed_tree.h
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef DIRECTEDGRAPH_H
#define DIRECTEDGRAPH_H
#include "template_utils.h"
namespace open_cpp_utils
{
template<typename T>
class directed_tree
{
// Forward Definitions =================================================================================================
public:
class breadth_first;
class pre_order;
class in_order;
class post_order;
private:
struct director;
// Typedefs ============================================================================================================
public:
using data_type = T;
using node = uint32_t;
using node_queue = std::deque<node>;
private:
using hierarchy = std::vector<director>;
using storage = std::vector<data_type>;
// Constants ===========================================================================================================
public:
static constexpr constant_value<node, node(0)> root{};
// Data Structures =====================================================================================================
private:
struct director
{
enum flags
{
VALID = 0x0001
};
node parent, child, prev_sibling, next_sibling;
uint32_t flags, depth;
director() : parent(0), child(0), prev_sibling(0), next_sibling(0), flags(VALID) { }
};
// Functions ===========================================================================================================
public:
// Constructors & Destructor ---------------------------------------------------------------------------------------
directed_tree() : graph_{ director() }, data_{ data_type() }, freed_{ } { }
// Tree Navigation -------------------------------------------------------------------------------------------------
[[nodiscard]] node parent(node id) const { return graph_[id].parent; }
[[nodiscard]] node first_child(node id) const { return graph_[id].child; }
[[nodiscard]] node prev_sibling(node id) const { return graph_[id].prev_sibling; }
[[nodiscard]] node next_sibling(node id) const { return graph_[id].next_sibling; }
[[nodiscard]] node left_most(node id) const
{
node current = id;
while(id = first_child(current)) current = id;
return current;
}
[[nodiscard]] uint32_t depth(node id) const
{
uint32_t depth = 0;
while (id)
{
id = parent(id);
++depth;
}
return depth;
}
// Tree Modification ---------------------------------------------------------------------------------------------------
node insert(const data_type& data, node p_id)
{
if(freed_.empty())
{
freed_.push_back(static_cast<node>(graph_.size()));
graph_.push_back(director()); data_.push_back(data);
}
node id = freed_.front(); freed_.pop_front();
director& node = graph_[id];
director& parent = graph_[p_id];
if(parent.child)
{
director& nchild = graph_[parent.child];
node.prev_sibling = nchild.prev_sibling;
nchild.prev_sibling = id;
if(nchild.prev_sibling)
{
director& pchild = graph_[nchild.prev_sibling];
pchild.next_sibling = id;
}
}
// Setup node
node.parent = p_id;
node.next_sibling = parent.child;
node.child = 0;
node.flags = director::VALID;
// Set parent's child
parent.child = id;
data_[id] = data;
return id;
}
void erase(node id)
{
if(id == 0) return;
director& erased = graph_[id];
erased.Flags &= ~director::VALID;
freed_.push_back(id);
graph_[erased.parent].Child = erased.Sibling;
node_queue stack{ erased.Child };
while(stack.empty() == false)
{
node next = stack.front(); stack.pop_front();
director& child = graph_[next];
child.Flags &= ~director::VALID;
freed_.push_back(next);
if(child.Sibling) stack.push_front(child.Sibling);
if(child.Child) stack.push_front(child.Child);
}
}
// Tree Access ---------------------------------------------------------------------------------------------------------
data_type& operator[](node id) { return data_[id]; }
[[nodiscard]] const data_type& operator[](node id) const { return data_[id]; }
// Visitor Pattern -----------------------------------------------------------------------------------------------------
template<typename O = pre_order, typename V>
void traverse(V& visitor)
{
traverser<V, O> traverser(*this, visitor);
traverser();
}
// Variables =======================================================================================================
private:
hierarchy graph_;
storage data_;
node_queue freed_;
// Navigation ======================================================================================================
public:
class breadth_first
{
public:
breadth_first(directed_tree& graph) : graph_(graph), visit_queue_(0) { }
node operator()(node node)
{
node = visit_queue_.back(); visit_queue_.pop_back();
director& current = graph_.graph_[node];
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 node;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
class pre_order
{
public:
pre_order(directed_tree& graph) : graph_(graph) { }
node operator()(node id)
{
director& 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_;
};
class in_order
{
public:
in_order(directed_tree& graph) : graph_(graph) { }
node operator()(node node)
{
if(node == 0) visit_queue_.push_back(graph_.left_most(node));
node = visit_queue_.front(); visit_queue_.pop_front();
director& current = graph_.graph_[node];
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 node;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
class post_order
{
public:
post_order(directed_tree& graph) : graph_(graph) { }
node operator()(node node)
{
if(visit_queue_.empty()) visit_queue_.push_back(graph_.left_most(node));
node = visit_queue_.front(); visit_queue_.pop_front();
if(node == 0) return node;
director& current = graph_.graph_[node];
visit_queue_.push_back(current.Sibling ? graph_.left_most(current.Sibling) : graph_.parent(node));
return node;
}
private:
directed_tree& graph_;
node_queue visit_queue_;
};
template<typename V, typename O>
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 node = 0;
while(node = order_(node))
{
if(visitor_(graph_[node], node)) break;
}
}
private:
directed_tree& graph_;
visitor_type& visitor_;
order_type order_;
};
};
}
#endif //DIRECTEDGRAPH_H
open_cpp_utils::directed_tree::breadth_first
Breadth first traversal.
Definition directed_tree.h:279
open_cpp_utils::directed_tree::in_order
In-order traversal.
Definition directed_tree.h:331
open_cpp_utils::directed_tree::post_order
Post-order traversal.
Definition directed_tree.h:361
open_cpp_utils::directed_tree::pre_order
Pre-order traversal.
Definition directed_tree.h:305
open_cpp_utils::directed_tree::traverser
Visitor pattern for traversing the tree.
Definition directed_tree.h:389
open_cpp_utils::directed_tree
Class for creating a directed tree.
Definition directed_tree.h:33
open_cpp_utils::directed_tree::erase
void erase(node id)
Erase a node in the tree.
Definition directed_tree.h:205
open_cpp_utils::directed_tree::insert
node insert(const data_type &data, node p_id)
Insert a node into the tree as a child of the provided node.
Definition directed_tree.h:160
open_cpp_utils::directed_tree::directed_tree
directed_tree()
Default constructor, creates tree with empty root.
Definition directed_tree.h:90
open_cpp_utils::directed_tree::prev_sibling
node prev_sibling(node id) const
Get the previous sibling of a node. O(1)
Definition directed_tree.h:114
open_cpp_utils::directed_tree::next_sibling
node next_sibling(node id) const
Get the next sibling of a node. O(1)
Definition directed_tree.h:121
open_cpp_utils::directed_tree::parent
node parent(node id) const
Get the parent of a node. O(1)
Definition directed_tree.h:100
open_cpp_utils::directed_tree::first_child
node first_child(node id) const
Get the first child of a node. O(1)
Definition directed_tree.h:107
open_cpp_utils::directed_tree::left_most
node left_most(node id) const
Get the left most child of a node. O(log(n))
Definition directed_tree.h:128
open_cpp_utils::directed_tree::depth
uint32_t depth(node id) const
Get the depth of a node.
Definition directed_tree.h:140
open_cpp_utils::directed_tree::traverse
void traverse(V &visitor)
Traverser-Visitor pattern for accessing the tree.
Definition directed_tree.h:256
open_cpp_utils::directed_tree::operator[]
const data_type & operator[](node id) const
Constant getter for data associated with a node.
Definition directed_tree.h:244
open_cpp_utils::directed_tree::operator[]
data_type & operator[](node id)
Getter for data associated with a node.
Definition directed_tree.h:237
open_cpp_utils::constant_value
Compile-time constant value.
Definition template_utils.h:34
template_utils.h
Provides compile time evaluation utilities for templates and template packs.