open-cpp-utils 0.0.1
Loading...
Searching...
No Matches
directed_tree.h
1// =====================================================================================================================
2// open-cpp-utils, an open-source cpp library with data structures that extend the STL.
3// Copyright (C) 2024 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
19#ifndef OPEN_CPP_UTILS_DIRECTED_TREE_H
20#define OPEN_CPP_UTILS_DIRECTED_TREE_H
21
22#include <vector>
23#include <deque>
24#include <algorithm>
25#include <cstring>
26
27namespace open_cpp_utils
28{
29
37template<typename T, class Alloc = std::allocator<T>>
39{
40// Forward Definitions =================================================================================================
41
42public:
43 class breadth_first;
44 class pre_order;
45 class in_order;
46 class post_order;
47 class unordered;
48
49private:
50 struct Node_;
51
52
53// Typedefs ============================================================================================================
54
55public:
56 using data_type = T;
57 using node = size_t;
58 using node_queue = std::deque<node>;
59
60private:
61 using s_alloc = Alloc;
62 using h_alloc = typename std::allocator_traits<s_alloc>::template rebind_alloc<Node_>; // Gross
63 using hierarchy = Node_*;
64 using storage = data_type*;
65
66
67// Constants ===========================================================================================================
68
69public:
70 static constexpr std::integral_constant<node, 0> root{};
71
72
73// Data Structures =====================================================================================================
74
75private:
76 struct Node_
77 {
78 enum flags
79 {
80 valid = 0x0001
81 };
82
83 node parent, child, prev_sibling, next_sibling;
84 uint32_t flags, depth;
85
86 Node_() : parent(0), child(0), prev_sibling(0), next_sibling(0), flags(0), depth(0) { }
87 };
88
89
90// Functions ===========================================================================================================
91
92private:
93
94// Helpers -------------------------------------------------------------------------------------------------------------
95
96 void grow_()
97 {
98 hierarchy g_old = graph_;
99 storage d_old = data_;
100 size_t c_old = capacity_;
101
102 if(capacity_ == 0) capacity_ = 10;
103 else capacity_ *= 2;
104
105 graph_ = g_alloc_.allocate(capacity_);
106 data_ = d_alloc_.allocate(capacity_);
107
108 if(size_ > 0)
109 {
110 std::memcpy(graph_, g_old, size_ * sizeof(Node_));
111 std::memcpy(data_, d_old, size_ * sizeof(data_type));
112
113 for(node i = size_; i < capacity_; ++i)
114 {
115 graph_[i] = Node_();
116 std::construct_at(data_ + i);
117 }
118 }
119 else
120 {
121 for(node i = 0; i < capacity_; ++i)
122 {
123 graph_[i] = Node_();
124 std::construct_at(data_ + i);
125 }
126
127 graph_[0].flags = Node_::valid;
128 }
129
130 g_alloc_.deallocate(g_old, c_old);
131 d_alloc_.deallocate(d_old, c_old);
132 }
133
134 node push_back_(const data_type& data)
135 {
136 if(size_ >= capacity_) grow_();
137 std::construct_at(data_ + size_, data);
138 return size_++;
139 }
140
141 node push_back_(data_type&& data)
142 {
143 if(size_ >= capacity_) grow_();
144 std::construct_at(data_ + size_, std::forward<T>(data));
145 return size_++;
146 }
147
148
149public:
150
151// Constructors & Destructor -------------------------------------------------------------------------------------------
152
157 : size_(0), capacity_(0)
158 , graph_(nullptr), data_(nullptr)
159 { push_back_(T()); }
160
161 directed_tree(data_type&& data)
162 : size_(0), capacity_(0)
163 , graph_(nullptr), data_(nullptr)
164 { push_back_(std::forward<T>(data)); }
165
166 directed_tree(const data_type& data)
167 : size_(0), capacity_(0)
168 , graph_(nullptr), data_(nullptr)
169 { push_back_(data); }
170
171 ~directed_tree() = default;
172
173
174// Tree Navigation -----------------------------------------------------------------------------------------------------
175
181 [[nodiscard]] bool valid(node id) const { return graph_ ? graph_[id].flags & Node_::valid : false; }
182
188 [[nodiscard]] node parent(node id) const { return graph_ ? graph_[id].parent : 0; }
189
195 [[nodiscard]] node first_child(node id) const { return graph_ ? graph_[id].child : 0; }
196
202 [[nodiscard]] node last_child(node id) const
203 {
204 node c = first_child(id);
205
206 while(c != 0) { if(graph_[c].next_sibling == 0) break; c = graph_[c].next_sibling; }
207
208 return c;
209 }
210
216 [[nodiscard]] node prev_sibling(node id) const { return graph_[id].prev_sibling; }
217
223 [[nodiscard]] node next_sibling(node id) const { return graph_[id].next_sibling; }
224
230 [[nodiscard]] node left_most(node id) const
231 {
232 node current = id;
233 while(id = first_child(current)) current = id;
234 return current;
235 }
236
242 [[nodiscard]] uint32_t depth(node id) const { return graph_[id].depth; }
243
244
245// Tree Modification ---------------------------------------------------------------------------------------------------
246
251 node next_id() const
252 {
253 if(freed_.empty()) return static_cast<node>(size_);
254 return freed_.front();
255 }
256
264 node insert(const data_type& data, node p_id, node sib = 0)
265 {
266 // If there are no freed nodes, create a new node and mark it as freed
267 if(freed_.empty())
268 {
269 freed_.push_back(push_back_(std::forward<T>(data)));
270 }
271 else
272 {
273 std::construct_at(data_ + freed_.front(), std::forward<T>(data));
274 }
275
276 // Pop a freed node from the stack
277 node id = freed_.front(); freed_.pop_front();
278 bool back = sib == 0;
279 node s_id = back ? last_child(p_id) : sib;
280 Node_& node = graph_[id];
281 Node_& parent = graph_[p_id];
282
283 Node_& sibling = graph_[s_id];
284 if(parent.child == root || (s_id == parent.child && !back)) parent.child = id;
285
286 node.next_sibling = node.prev_sibling = 0;
287 node.parent = p_id;
288 node.depth = parent.depth + 1;
289 node.flags = Node_::valid;
290 node.child = 0;
291
292 if(s_id == 0) return id;
293
294 if(back)
295 {
296 node.next_sibling = sibling.next_sibling;
297 node.prev_sibling = s_id;
298
299 sibling.next_sibling = id;
300 }
301 else
302 {
303 node.next_sibling = s_id;
304 node.prev_sibling = sibling.prev_sibling;
305
306 sibling.prev_sibling = id;
307 }
308
309 return id;
310 }
311
319 node insert(data_type&& data, node p_id, node sib = root)
320 {
321 // If there are no freed nodes, create a new node and mark it as freed
322 if(freed_.empty())
323 {
324 freed_.push_back(push_back_(std::forward<T>(data)));
325 }
326 else
327 {
328 std::construct_at(data_ + freed_.front(), std::forward<T>(data));
329 }
330
331 // Pop a freed node from the stack
332 node id = freed_.front(); freed_.pop_front();
333 bool back = sib == root;
334 node s_id = back ? last_child(p_id) : sib;
335 Node_& node = graph_[id];
336 Node_& parent = graph_[p_id];
337
338 Node_& sibling = graph_[s_id];
339 if(parent.child == root || (s_id == parent.child && !back)) parent.child = id;
340
341 node.next_sibling = node.prev_sibling = 0;
342 node.parent = p_id;
343 node.depth = parent.depth + 1;
344 node.flags = Node_::valid;
345 node.child = 0;
346
347 if(s_id == 0) return id;
348
349 if(back)
350 {
351 node.next_sibling = sibling.next_sibling;
352 node.prev_sibling = s_id;
353
354 sibling.next_sibling = id;
355 }
356 else
357 {
358 node.next_sibling = s_id;
359 node.prev_sibling = sibling.prev_sibling;
360
361 sibling.prev_sibling = id;
362 }
363
364 return id;
365 }
366
367 void swap(node a, node b)
368 {
369 Node_& A = graph_[a];
370 Node_& B = graph_[b];
371
372 std::swap(A, B);
373
374 if(graph_[B.parent].child == a) graph_[B.parent].child = b;
375 if(graph_[A.parent].child == b) graph_[A.parent].child = a;
376 }
377
378 void clear()
379 {
380 for(int i = 0; i < size_; ++i)
381 {
382 if(valid(i) == false) continue;
383
384 graph_[i].flags = 0;
385 std::destroy_at(data_ + i);
386 freed_.push_back(i);
387 }
388
389 g_alloc_.deallocate(graph_, capacity_);
390 d_alloc_.deallocate(data_, capacity_);
391 capacity_ = 0; size_ = 0;
392 }
393
398 void erase(node id)
399 {
400 if(id == root) return;
401
402 // Mark node as invalid and push it to the freed list
403 Node_& erased = graph_[id];
404 erased.flags = 0;
405 freed_.push_back(id);
406 std::destroy_at(data_ + id);
407
408 // Update the parent's child
409 graph_[erased.parent].child = erased.next_sibling;
410
411 // Update siblings
412 if(erased.next_sibling) graph_[erased.next_sibling].prev_sibling = erased.prev_sibling;
413 if(erased.prev_sibling) graph_[erased.prev_sibling].next_sibling = erased.next_sibling;
414
415 // Erase children - essentially breadth first propagation down the tree
416 node_queue stack{ erased.child };
417 while(stack.empty() == false)
418 {
419 node next = stack.front(); stack.pop_front();
420 Node_& child = graph_[next];
421 child.flags = 0;
422 freed_.push_back(next);
423 std::destroy_at(data_ + next);
424
425 if(child.next_sibling) stack.push_front(child.next_sibling);
426 if(child.child) stack.push_front(child.child);
427 }
428 }
429
430
431// Tree Access ---------------------------------------------------------------------------------------------------------
432
438 data_type& operator[](node id) { return data_[id]; }
439
445 [[nodiscard]] const data_type& operator[](node id) const { return data_[id]; }
446
447
448// Visitor Pattern -----------------------------------------------------------------------------------------------------
449
456 template<typename O = pre_order, typename V>
457 void traverse(V& visitor)
458 {
459 traverser<V, O> traverser(*this, visitor);
460 traverser();
461 }
462
463
464// Variables =======================================================================================================
465
466private:
467 h_alloc g_alloc_;
468 s_alloc d_alloc_;
469 size_t size_, capacity_;
470 hierarchy graph_;
471 storage data_;
472 node_queue freed_;
473
474
475// Navigation ======================================================================================================
476
477public:
478
480 {
481 public:
482 unordered(directed_tree& graph) : graph_(graph), current_(root) { }
483
484 node operator()(node id)
485 {
486 while(!graph_.valid(current_) || current_ == root)
487 {
488 ++current_;
489 }
490
491 id = current_;
492 current_ ++;
493
494 return id == graph_.graph_.size() ? 0 : id;
495 }
496
497 private:
498 directed_tree& graph_;
499 node current_;
500 };
501
506 {
507 public:
508 breadth_first(directed_tree& graph) : graph_(graph), visit_queue_(0) { }
509
510 node operator()(node id)
511 {
512 id = visit_queue_.back(); visit_queue_.pop_back();
513 Node_& current = graph_.graph_[id];
514
515 if(current.next_sibling) visit_queue_.push_back(current.next_sibling);
516 if(current.child) visit_queue_.push_front(current.child);
517
518 if(visit_queue_.empty()) return 0;
519 return id;
520 }
521
522 private:
523 directed_tree& graph_;
524 node_queue visit_queue_;
525 };
526
527
532 {
533 public:
534 pre_order(directed_tree& graph) : graph_(graph) { }
535
536 node operator()(node id)
537 {
538 Node_& current = graph_.graph_[id];
539
540 if(current.next_sibling) visit_queue_.push_front(current.next_sibling);
541 if(current.child) visit_queue_.push_front(current.child);
542
543 if(visit_queue_.empty()) return 0;
544 node next = visit_queue_.front(); visit_queue_.pop_front();
545 return next;
546 }
547
548 private:
549 directed_tree& graph_;
550 node_queue visit_queue_;
551 };
552
553
558 {
559 public:
560 in_order(directed_tree& graph) : graph_(graph) { }
561
562 node operator()(node id)
563 {
564 if(id == 0) visit_queue_.push_back(graph_.left_most(id));
565
566 id = visit_queue_.front(); visit_queue_.pop_front();
567 Node_& current = graph_.graph_[id];
568
569 if(current.Sibling)
570 {
571 if(graph_.next_sibling(current.Sibling)) visit_queue_.push_back(current.parent);
572 visit_queue_.push_back(graph_.left_most(current.Sibling));
573 }
574
575 return id;
576 }
577
578 private:
579 directed_tree& graph_;
580 node_queue visit_queue_;
581 };
582
583
588 {
589 public:
590 post_order(directed_tree& graph) : graph_(graph) { }
591
592 node operator()(node id)
593 {
594 if(visit_queue_.empty()) visit_queue_.push_back(graph_.left_most(id));
595
596 id = visit_queue_.front(); visit_queue_.pop_front();
597 if(id == 0) return id;
598 Node_& current = graph_.graph_[id];
599
600 visit_queue_.push_back(current.Sibling ? graph_.left_most(current.Sibling) : graph_.parent(id));
601
602 return id;
603 }
604
605 private:
606 directed_tree& graph_;
607 node_queue visit_queue_;
608 };
609
610
614 template<typename V, typename O>
616 {
617 public:
618 using visitor_type = V;
619 using order_type = O;
620
621 traverser(directed_tree& graph, visitor_type& visitor) : graph_(graph), visitor_(visitor), order_(graph) { }
622
623 void operator()()
624 {
625 node id = 0;
626 while(id = order_(id))
627 {
628 if(visitor_(graph_[id], id)) break;
629 }
630 }
631
632 private:
633 directed_tree& graph_;
634 visitor_type& visitor_;
635 order_type order_;
636 };
637};
638}
639
640#endif // OPEN_CPP_UTILS_DIRECTED_TREE_H
Breadth first traversal.
Definition directed_tree.h:506
In-order traversal.
Definition directed_tree.h:558
Post-order traversal.
Definition directed_tree.h:588
Pre-order traversal.
Definition directed_tree.h:532
Visitor pattern for traversing the tree.
Definition directed_tree.h:616
Definition directed_tree.h:480
Class for creating a directed tree.
Definition directed_tree.h:39
node prev_sibling(node id) const
Get the previous sibling of a node. O(1)
Definition directed_tree.h:216
node parent(node id) const
Get the parent of a node. O(1)
Definition directed_tree.h:188
uint32_t depth(node id) const
Get the depth of a node.
Definition directed_tree.h:242
node first_child(node id) const
Get the first child of a node. O(1)
Definition directed_tree.h:195
node next_sibling(node id) const
Get the next sibling of a node. O(1)
Definition directed_tree.h:223
node left_most(node id) const
Get the left most child of a node. O(log(n))
Definition directed_tree.h:230
const data_type & operator[](node id) const
Constant getter for data associated with a node.
Definition directed_tree.h:445
node last_child(node id) const
Get the first child of a node. O(1)
Definition directed_tree.h:202
data_type & operator[](node id)
Getter for data associated with a node.
Definition directed_tree.h:438
node next_id() const
Get the next id that would be used if insert() were called.
Definition directed_tree.h:251
bool valid(node id) const
Check whether a node is valid. O(1)
Definition directed_tree.h:181
void erase(node id)
Erase a node in the tree. O(n)
Definition directed_tree.h:398
node insert(data_type &&data, node p_id, node sib=root)
Insert a node into the tree as a child of the provided node.
Definition directed_tree.h:319
void traverse(V &visitor)
Traverser-Visitor pattern for accessing the tree.
Definition directed_tree.h:457
directed_tree()
Default constructor, creates tree with empty root.
Definition directed_tree.h:156
node insert(const data_type &data, node p_id, node sib=0)
Insert a node into the tree as a child of the provided node.
Definition directed_tree.h:264