- Binary Tree (Partial)

- Sequence (Partial)
This commit is contained in:
2025-08-30 22:11:41 -04:00
parent 992a02db3e
commit dbcb50349d
17 changed files with 1133 additions and 98 deletions

View File

@@ -34,30 +34,38 @@
#include <fennec/containers/dynarray.h>
#include <fennec/containers/list.h>
#include <fennec/containers/optional.h>
#include <fennec/containers/traversal.h>
#include <fennec/math/exponential.h>
#include <fennec/memory/allocator.h>
namespace fennec
{
template<typename TypeT, class AllocT>
///
/// \brief Structure defining a binary tree
/// \tparam TypeT The data type
/// \tparam AllocT An allocator class
template<typename TypeT, class AllocT = allocator<TypeT>>
struct bintree {
// Definitions =========================================================================================================
// Definitions =========================================================================================================
protected:
struct node;
public:
using value_t = TypeT;
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
static constexpr size_t root = 0;
static constexpr size_t npos = -1;
inline static size_t sink = npos;
friend class iterator;
friend class const_iterator;
protected:
struct node {
optional<value_t> value;
size_t parent, left, right;
size_t depth;
value_t value;
size_t parent, left, right;
size_t depth;
constexpr node()
: value(nullopt)
@@ -83,18 +91,103 @@ protected:
using table_t = allocation<node, alloc_t>;
using freed_t = list<size_t, alloc_t>;
// Constructors & Destructor ===========================================================================================
public:
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes an empty tree
constexpr bintree()
: _table()
, _freed()
, _root(npos)
, _size(0) {
}
///
/// \brief Move Constructor, takes ownership of a tree
/// \param tree The tree to take ownership of
constexpr bintree(bintree&& tree) noexcept
: _table(fennec::move(tree._table))
, _freed(fennec::move(tree._freed))
, _root(tree._root)
, _size(tree._size) {
}
///
/// \brief Copy Constructor, copies a tree
/// \param tree The tree to copy
constexpr bintree(const bintree& tree)
: _table(tree._table)
, _freed(tree._freed)
, _root(tree._root)
, _size(tree._size) {
}
///
/// \brief Destructor, clears the tree
constexpr ~bintree() {
clear();
}
/// @}
// Properties ==========================================================================================================
public:
///
/// \returns The number of elements in the tree
constexpr size_t size() const {
return _size;
}
///
/// \returns The capacity of the underlying allocation
constexpr size_t capacity() const {
return _table.capacity();
}
///
/// \returns The next id to be returned by `insert` or `emplace`.
constexpr size_t next_id() const {
size_t i = _size;
if (not _freed.empty()) {
i = _freed.front();
}
return i;
}
///
/// \returns The next id to be returned by `insert` or `emplace`.
constexpr size_t root() const {
return _root;
}
// Navigation ==========================================================================================================
public:
/// \name Navigation
/// @{
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns The parent of node `i`
constexpr size_t parent(size_t i) const {
if (i >= _table.size()) {
return false;
}
return _table[i].parent;
return i >= _table.size() ? npos : _table[i].parent;
}
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns The grandparent of node `i`
constexpr size_t grandparent(size_t i) const {
return parent(parent(i));
}
///
@@ -102,10 +195,7 @@ public:
/// \param i The node id
/// \returns The left child of node `i`
constexpr size_t left(size_t i) const {
if (i >= _table.size()) {
return false;
}
return _table[i].left;
return i >= _table.size() ? npos : _table[i].left;
}
///
@@ -113,10 +203,26 @@ public:
/// \param i The node id
/// \returns The right child of node `i`
constexpr size_t right(size_t i) const {
if (i >= _table.size()) {
return false;
}
return _table[i].right;
return i >= _table.size() ? npos : _table[i].right;
}
///
/// \brief \f$O(1)\f$
/// \param i The id of the node
/// \returns The id of the sibling of `i`
constexpr size_t sibling(size_t i) const {
size_t p = parent(i);
size_t l = left(p);
size_t r = right(p);
return i == l ? l : r;
}
///
/// \brief Short for "Parent Sibling," \f$O(1)\f$
/// \param i The id of the node
/// \returns The id of the parents' sibling of `i`
constexpr size_t parsib(size_t i) const {
return sibling(parent(i));
}
///
@@ -124,14 +230,45 @@ public:
/// \param i The node id
/// \returns The depth of node `i`
constexpr size_t depth(size_t i) const {
return i >= _table.size() ? npos : _table[i].depth;
}
///
/// \brief \f$O(\log n)\f$
/// \param i The node id
/// \returns The id of the left-most node of `i`
constexpr size_t left_most(size_t i) const {
if (i >= _table.size()) {
return npos;
}
return _table[i].depth;
while (_table[i].left != npos) {
i = _table[i].left;
}
return i;
}
///
/// \brief \f$O(\log n)\f$
/// \param i The node id
/// \returns The id of the right-most node of `i`
constexpr size_t right_most(size_t i) const {
if (i >= _table.size()) {
return npos;
}
while (_table[i].right != npos) {
i = _table[i].right;
}
return i;
}
/// @}
// Access ==============================================================================================================
public:
/// \name Access
/// @{
///
/// \details \f$O(1)\f$
@@ -155,8 +292,13 @@ public:
return _table[i] ? &*_table[i] : nullptr;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move Left Insertion, constructs a new node as the left child of `p`
/// \details If the left node of `p` already exists, the move assignment operator is used instead
@@ -174,11 +316,11 @@ public:
/// \param val The object to copy to the new node
/// \returns The id of the new node
constexpr size_t insert_left(size_t p, const value_t& val) {
return this->_insert_left(p,, val);
return this->_insert_left(p, val);
}
///
/// \brief Move Left Insertion, constructs a new node as the left child of `p`
/// \brief Emplace Left Insertion, constructs a new node as the left child of `p`
/// \details If the left node of `p` already exists, the move assignment operator is used instead
/// \param p The parent node
/// \param args The arguments to construct the new node with
@@ -188,6 +330,37 @@ public:
return this->_insert_left(p, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Move Right Insertion, constructs a new node as the right child of `p`
/// \details If the right node of `p` already exists, the move assignment operator is used instead
/// \param p The parent node
/// \param val The object to move into the new node
/// \returns The id of the new node
constexpr size_t insert_right(size_t p, value_t&& val) {
return this->_insert_right(p, fennec::forward<value_t>(val));
}
///
/// \brief Copy Right Insertion, constructs a new node as the right child of `p`
/// \details If the right node of `p` already exists, the copy assignment operator is used instead
/// \param p The parent node
/// \param val The object to copy to the new node
/// \returns The id of the new node
constexpr size_t insert_right(size_t p, const value_t& val) {
return this->_insert_right(p, val);
}
///
/// \brief Emplace Right Insertion, constructs a new node as the right child of `p`
/// \details If the right node of `p` already exists, the move assignment operator is used instead
/// \param p The parent node
/// \param args The arguments to construct the new node with
/// \returns The id of the new node
template<typename...ArgsT>
constexpr size_t emplace_right(size_t p, ArgsT&&...args) {
return this->_insert_right(p, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Perform a Left Tree Rotation at `i`
/// \param i The root node for the rotation
@@ -242,55 +415,345 @@ public:
_table[l].right = r;
}
///
/// \brief Clears the tree, destroying all elements
constexpr void clear() {
list<size_t> queue;
if (_root != npos) {
queue.push_back(_root);
}
while (not queue.empty()) {
size_t i = queue.front();
queue.pop_front();
if (_table[i].left != npos) {
queue.push_front(_table[i].left);
}
if (_table[i].right != npos) {
queue.push_front(_table[i].right);
}
fennec::destruct(&_table[i]);
}
_size = 0;
_root = npos;
}
/// @}
// Traversal ===========================================================================================================
///
/// \brief Traverse the tree using a specified order and visiting functor
///
/// \details
/// The visitor should accept a reference to a value of type `TypeT` and a `size_t` which contains the node's id.
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
///
/// \tparam OrderT The order with which to traverse the tree.
/// \tparam VisitorT The visitor, should fulfill the signature `uint8_t visit(TypeT&, size_t)`
/// \param visit The visiting object
/// \param i The node to start at
template<typename OrderT, typename VisitorT>
constexpr void traverse(VisitorT&& visit, size_t i = root) {
OrderT order;
i = order(*this, i);
while (i != npos) {
uint8_t mode = traversal_control_continue;
if (_table[i].value) {
mode = visit(*_table[i].value, i);
}
if (mode == traversal_control_break) {
break;
}
i = order[*this, i, mode];
}
}
struct breadth_first {
list<size_t> visit;
size_t head;
size_t operator()(const bintree&, size_t start) {
return head = start;
}
size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
if (node == npos) {
return npos;
}
size_t lft = tree.left(tree.parent(node));
size_t nxt = lft == node ? tree.right(tree.parent(node)) : npos;
size_t chd = tree.left(node);
if (nxt != npos && node != head) {
visit.push_front(nxt);
}
if (chd != npos && mode != traversal_control_jump_over) {
visit.push_back(chd);
}
if (not visit.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
struct pre_order {
list<size_t> visit;
size_t head;
constexpr size_t operator()(const bintree&, size_t start) {
head = start;
return start;
}
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
if (node == npos) {
return npos;
}
size_t nxt = tree.sibling(node);
size_t chd = tree.left(node);
nxt = node == nxt ? npos : nxt;
if (nxt != npos && node != head) {
visit.push_front(nxt);
}
if (chd != npos && mode != traversal_control_jump_over) {
visit.push_front(chd);
}
if (not visit.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
struct in_order {
list<size_t> visit;
size_t head;
constexpr size_t operator()(const bintree& tree, size_t start) {
head = start;
return tree.left_most(start);
}
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
if (node == npos) {
return npos;
}
size_t prnt = tree.parent(node);
size_t next = tree.sibling(node);
next = node == next ? npos : next;
if (node != head) {
if (tree.left(prnt) == node) {
visit.push_back(prnt);
if (next != npos) {
visit.push_back(tree.left_most(next));
}
} else if (next != npos) {
visit.push_front(tree.left_most(next));
}
}
if (not visit.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
struct post_order {
list<size_t> visit;
size_t head;
constexpr size_t operator()(const bintree& tree, size_t start) {
head = start;
return tree.left_most(start);
}
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
if (node == npos) {
return npos;
}
size_t prnt = tree.parent(node);
size_t next = tree.sibling(node);
next = node == next ? npos : next;
if (node != head) {
if (next != npos) {
visit.push_front(tree.left_most(next));
} else {
visit.push_front(prnt);
}
}
if (not visit.empty()) {
node = visit.front();
visit.pop_front();
} else {
node = npos;
}
return node;
}
};
// Iterator ============================================================================================================
class iterator {
protected:
bintree* _tree;
in_order _order;
size_t _n;
public:
constexpr iterator(bintree* tree, size_t root, size_t node)
: _tree(tree)
, _order()
, _n(node) {
_order(*tree, root);
}
iterator& operator++() {
return _n = _order[*_tree, _n, traversal_control_continue], *this;
}
value_t& operator*() {
return _tree[_n];
}
value_t* operator->() {
return &_tree[_n];
}
const value_t& operator*() const {
return _tree[_n];
}
const value_t* operator->() const {
return &_tree[_n];
}
constexpr bool operator==(const iterator& it) {
return _tree == it._tree and _n == it._n;
}
constexpr bool operator!=(const iterator& it) {
return _tree != it._tree or _n != it._n;
}
};
// Fields ==============================================================================================================
protected:
table_t _table;
freed_t _freed;
size_t _root, _size;
constexpr void _next_free() {
// Helpers =============================================================================================================
constexpr size_t _next_free() {
size_t i = _size;
if (not _freed.empty()) {
i = _freed.front();
_freed.pop_front();
}
if (i >= _table.size()) {
_table.reallocate(2 * fennec::max(_table.size(), 4));
if (i >= _table.capacity()) {
_table.creallocate(2 * fennec::max(_table.capacity(), size_t(4)));
}
++_size;
return i;
}
template<typename...ArgsT>
constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
size_t i = left(p);
if (i == npos) {
i = _next_free();
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
size_t i = p == npos ? _root : left(p);
if (i != npos) {
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
} else {
size_t d = 1;
i = _next_free();
if (p != npos) {
d = depth(p) + 1;
_table[p].left = i;
}
fennec::construct(&_table[i], p, npos, npos, d);
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
}
return i;
}
template<typename...ArgsT>
constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
size_t i = right(p);
if (i == npos) {
i = _next_free();
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
size_t i = p == npos ? _root : right(p);
if (i != npos) {
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
if (p == npos || _root == npos) {
_root = i;
}
} else {
size_t d = 1;
i = _next_free();
if (p != npos) {
d = depth(p) + 1;
_table[p].right = i;
}
fennec::construct(&_table[i], p, npos, npos, d);
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
}
return i;
}
constexpr size_t& parent(size_t i) {
return i >= _table.size() ? sink : _table[i].parent;
}
constexpr size_t& grandparent(size_t i) {
return parent(parent(i));
}
constexpr size_t& left(size_t i) {
return i >= _table.size() ? sink : _table[i].left;
}
constexpr size_t& right(size_t i) {
return i >= _table.size() ? sink : _table[i].right;
}
constexpr size_t& sibling(size_t i) {
size_t p = parent(i);
size_t& l = left(p);
size_t& r = right(p);
return i == l ? l : r;
}
constexpr size_t& parsib(size_t i) {
return sibling(parent(i));
}
};