C++ Library Extensions 2022.12.09
To help learn modern C++ programming
053-binary_tree05.cpp
Go to the documentation of this file.
1#include <tpf_output.hpp>
2
3/*
4 binary_tree 50 17 72 12 23 54 76 9 14 19 67 > old.txt
5
6 binary_tree 50 17 72 12 23 54 76 9 14 19 67 25 > new.txt
7
8 binary_tree 50 17 72 12 23 54 76 9 14 19 67 25 18> newnew.txt
9
10 https://edotor.net/
11
12 */
13
15
16auto endl = tpf::endl; // single carriage return and flush to console output
17auto endL = tpf::endL; // double carriage returns and flush to console output
18
19auto nl = tpf::nl; // single carriage return without flush
20auto nL = tpf::nL; // double carriage returns without flush
21
22using string_t = std::string;
23
24template<typename ReturnType, typename... Types>
26 std::enable_if_t<tpf::types::is_same_v<tpf::remove_cv_ref_t<Types>...>, ReturnType>;
27
28template<typename ElementType>
29class binary_node
30{
31 public:
32 using node_ptr_t = std::unique_ptr<binary_node>;
33 enum class visit_mode: int{ undefined = 0, pre_order, in_order,
34 ascending_order = in_order, post_order, descending_order };
35
36 enum class find_mode: int { undefined = 0, predecessor = 1, exact_match = 2, successor = 3 };
37
38 enum class child_status: int { left_child = -1, no_child = 0, right_child = 1};
39 private:
40
41 ElementType m_value; // node's value
42
43 mutable int m_height{1}; // the height of a node
44
45 binary_node* m_parent; // pointer to point to the parent node
46
47 node_ptr_t m_left; // left child node
48 node_ptr_t m_right; // right child node
49
50 public:
51
52 int height(bool bRecalculate = false) const
53 {
54 if(bRecalculate) // if bRecalculate is true,
55 { // we will recalculate the m_height member
56 int left_height = 1, right_height = 1;
57
58 if(this->m_left)
59 left_height += this->m_left->height(false); // false means we use cached value of m_height
60
61 if(this->m_right)
62 right_height += this->m_right->height(false); // false means we use cached value of m_height
63
64 // m_height is the greater of the two, left_height and right_height
65 this->m_height = left_height > right_height ?
66 left_height : right_height;
67 }
68
69 return this->m_height;
70 }
71
73 {
74 // the parent ptr of the current node
75 auto parent_ptr = this->m_parent;
76
77 while(parent_ptr)
78 {
79 auto old_height = parent_ptr->m_height;
80
81 if(old_height != parent_ptr->height(true))
82 parent_ptr = parent_ptr->m_parent;
83 else
84 break;
85 }
86 }
87
89 {
90 if(this->m_left && this->m_left.get() == child)
91
93 else if(this->m_right && this->m_right.get() == child)
94
96 else
98 }
99
101 {
102 os << "node_" << this->m_value;
103 return os;
104 }
105
107 {
108 // node_1 [ shape=box, label = " Root "] ;
109 get_node_name(os)
110 << " [ shape=oval, label = \"V:"
111 << this->m_value
112 << " H:"<< this->m_height << "\"] ;";
113
114 return os;
115 }
116
118 {
119 get_node_definition(os) << nl;
120
121 if(this->m_parent)
122 {
123 get_node_name(os) << " -> ";
124 this->m_parent->get_node_name(os);
125
126 auto status = this->m_parent->get_child_status(this);
127
128 if(status == child_status::left_child)
129 os << " [style = dashed, color = red] ; " << nl;
130 else if (status == child_status::right_child)
131 os << " [style = dashed, color = blue] ; " << nl;
132 }
133
134 // root -> left
135 if(this->m_left)
136 {
137 get_node_name(os) << " -> ";
138 this->m_left->get_node_name(os) << " [ color = red ] ;" << nl;
139
140 // recursion for left children
141 this->m_left->print_node(os);
142 }
143
144 // root -> right
145 if(this->m_right)
146 {
147 get_node_name(os) << " -> ";
148 this->m_right->get_node_name(os) << " [color = blue ] ; " << nl;
149
150 // recursion for right children
151 this->m_right->print_node(os);
152 }
153 }
155 {
156 tpf::sstream os;
157
158 os << "digraph G { " << nl;
159
160 print_node(os);
161
162 os <<"}";
163
164 return os.str();
165 }
166
167 const ElementType& get() const { return this->m_value; }
168
169 // if find() fails, returns nullptr,
170 // if successful, it returns the pointer that points
171 // to the node that has the value
172 binary_node* find(ElementType value)
173 {
174 if(value == this->m_value)
175 return this;
176
177 binary_node* ptr = nullptr;
178
179 // recursion
180 if(this->m_left && (ptr = this->m_left->find(value)))
181 return ptr;
182 // recursion
183 if(this->m_right && (ptr = this->m_right->find(value)))
184 return ptr;
185
186 return nullptr;
187 }
188
189 binary_node(ElementType value = ElementType{}, binary_node* parent = nullptr):
190 m_value{value}, m_parent{parent} { }
191
192 // if insertion fails, returns false
193
194 template<typename Type>
195 // we enable this function only when Type and ElementType are same
197 insert(Type&& value)
198 {
199 if(value == this->m_value)
200 return false;
201 else if(value < this->m_value)
202 {
203 if(this->m_left)
204 // this is recursion
205 return this->m_left->insert(std::forward<Type>(value));
206 else
207 {
208 // a new node is inserted and set to this->m_left member
209 // new inserted node is a Leaf node.
210 this->m_left.reset(new binary_node{std::forward<Type>(value), this} );
211
212 // this call updates all m_height members of its parents
213 this->m_left->update_height();
214
215 return true;
216 }
217 }
218 else // value > this->m_value
219 {
220 if(this->m_right)
221 // this is also recursion
222 return this->m_right->insert(std::forward<Type>(value));
223 else
224 {
225 // a new node is inserted and set to this->m_right member
226 // the new inserted node is a LEAF node
227 this->m_right.reset( new binary_node{std::forward<Type>(value), this});
228
229 // update all m_height members of its parents
230 this->m_right->update_height();
231
232 return true;
233 }
234 }
235 }
236
237 bool graft(node_ptr_t node_ptr) // node_ptr_t == std::unique_ptr<binary_node>
238 {
239 if( node_ptr->m_value == this->m_value)
240 return false;
241 else if(node_ptr->m_value < this->m_value)
242 {
243 if(this->m_left)
244 // this is recursion
245 return this->m_left->graft( std::move(node_ptr) );
246 else
247 {
248 node_ptr->m_parent = this;
249 this->m_left = std::move(node_ptr);
250 this->m_left->update_height();
251
252 return true;
253 }
254 }
255 else // node_ptr->m_value > this->m_value
256 {
257 if(this->m_right)
258 // this is also recursion
259 return this->m_right->graft(std::move(node_ptr));
260 else
261 {
262 node_ptr->m_parent = this;
263 this->m_right = std::move(node_ptr);
264 this->m_right->update_height();
265
266 return true;
267 }
268 }
269 }
270
271 // return true only if all parameters are inserted
272 // successfully, otherwise returns false
273 template<typename Type, typename... Types>
274 // we enable this function only when ElementType, Type, and Types... are
275 // of same type
276 enable_if_all_types_are_the_same_t<bool, ElementType, Type, Types...>
277 insert(Type&& arg, Types&&... args)
278 {
279 bool result = this->insert(std::forward<Type>(arg));
280
281 if constexpr(sizeof...(args) != 0)
282 {
283 // recursion
284 return result && this->insert(std::forward<Types>(args)...);
285 }
286 else
287 return result;
288 }
289
290 // for more information about visiting order
291 // Binary Tree (ALL Interview Questions)
292 // https://www.youtube.com/watch?v=VQTF_pRTZek&list=PLeIMaH7i8JDj7DnmO7lll97P1yZjMCpgY&index=2
294 {
295 switch(order)
296 {
298 {
299 os << this->m_value << ", ";
300
301 if(this->m_left)
302 this->m_left->visit_nodes(os, order);
303
304 if(this->m_right)
305 this->m_right->visit_nodes(os, order);
306
307 return;
308 }
310 {
311 if(this->m_left)
312 this->m_left->visit_nodes(os, order);
313
314 if(this->m_right)
315 this->m_right->visit_nodes(os, order);
316
317 os << this->m_value << ", ";
318
319 return;
320 }
321
323 {
324 if(this->m_right)
325 this->m_right->visit_nodes(os, order);
326
327 os << this->m_value << ", ";
328
329 if(this->m_left)
330 this->m_left->visit_nodes(os, order);
331
332 return;
333 }
334
336 default: // in_order
337 {
338 if(this->m_left)
339 this->m_left->visit_nodes(os, order);
340
341 os << this->m_value << ", ";
342
343 if(this->m_right)
344 this->m_right->visit_nodes(os, order);
345
346 return;
347 }
348 }
349 }
350
351 // if fails, return nullptr
352 binary_node* find(ElementType value, find_mode fmode,
354 {
355 binary_node* ptr = nullptr;
356
357 if(((vmode==visit_mode::ascending_order) && (fmode==find_mode::successor))||
359 {
360 if(this->m_left && (ptr = this->m_left->find(value, fmode, vmode)))
361 return ptr;
362
363 if( value < this->m_value)
364 return this;
365
366 if(this->m_right && (ptr = this->m_right->find(value, fmode, vmode)))
367 return ptr;
368
369 // we failed
370 return ptr;
371 }
372 else if(( (vmode == visit_mode::ascending_order)&&(fmode==find_mode::predecessor)) ||
374 {
375 if(this->m_right && (ptr = this->m_right->find(value, fmode, vmode)))
376 return ptr;
377
378 if(value > this->m_value)
379 return this;
380
381 if(this->m_left && (ptr = this->m_left->find(value, fmode, vmode)))
382 return ptr;
383
384 return ptr;
385 }
386 else if(fmode == find_mode::exact_match)
387 return this->find(value);
388 else
389 return nullptr;
390 }
391
392 static bool remove_node(node_ptr_t& root_ptr, ElementType value)
393 {
394 binary_node* ptr = root_ptr->find(value);
395
396 // node with "value" does not exist
397 if(!ptr) return false;
398
399 // if parent is nullptr, then it is root node
400 if(!ptr->m_parent)
401 {
402 // ptr is a root node of type binary_node*
403 // root_ptr is a root node of type
404 // std::unique_ptr<binary_node>;
405
406 // we move left child to left_child
407 // after move, root_ptr->m_left is invalid
408 node_ptr_t left_child
409 = std::move(root_ptr->m_left);
410
411 // after move, root_ptr->m_right is invalid
412 node_ptr_t right_child
413 = std::move(root_ptr->m_right);
414
415 if(right_child) // new root
416 {
417 // because root node does not have its parent.
418 right_child->m_parent = nullptr;
419
420 // existing root_ptr is destroyed
421 // and right_child is moved to root_ptr
422 // at this point, existing old root is
423 // properly released.
424 root_ptr = std::move(right_child);
425
426 // do not forget this part
427 if(left_child)
428 root_ptr->graft(std::move(left_child));
429
430 return true;
431 }
432 else if(left_child) // right_child is nullptr
433 // left_child is a new root.
434 {
435 // because root node does not have its parent.
436 left_child->m_parent = nullptr;
437
438 // after move, the older root is released
439 // and left_child is moved to root_ptr
440 root_ptr = std::move(left_child);
441
442 return true;
443 }
444 else
445 {
446 // since we destroyed the root
447 // we should not access the root after this statment
448 root_ptr.reset();
449
450 return true;
451 }
452 }
453 else // non-root node
454 {
455
456 return true;
457 }
458 }
459}; // end of binary_node class
460
461// std::unique_ptr<binary_node<ElementType>>
462template<typename ElementType>
464
465template<typename ElementType>
466void test_remove_node(node_ptr_t<ElementType>& node_ptr, ElementType value)
467{
468 using binary_node_t = binary_node<ElementType>;
469 using node_ptr_t = std::unique_ptr<binary_node_t>;
470
471 binary_node_t::remove_node(node_ptr, value);
472
473 if(node_ptr)
474 {
475 stream << node_ptr->build_digraph() << endl;
476 }
477
478}
479
481{
482 // In courtesy of Vivekanand Khyade - Algorithm Every Day
483 // Introduction to AVL tree (Why AVL tree is needed?)
484 // https://youtu.be/5Q-__zhQ2Gs?t=10
485
486 binary_node<int> root{10};
487
489
490 root.insert(8);
491 root.insert(15);
492
493 root.insert(7);
494 root.insert(9);
495 root.insert(12);
496 root.insert(17);
497
498 root.insert(6);
499 root.insert(16);
500 root.insert(18);
501
502 tpf::sstream os;
503
504 root.visit_nodes(os, visit_mode::ascending_order);
505 stream << "Ascending-order: " << endL;
506 stream << os.str() << endL;
507
508 os.clear();
509
510 root.visit_nodes(os, visit_mode::descending_order);
511 stream << "Descending-order: " << endL;
512 stream << os.str() << endL;
513}
514
516{
517 // In courtesy of Vivekanand Khyade - Algorithm Every Day
518 // Introduction to AVL tree (Why AVL tree is needed?)
519 // https://youtu.be/5Q-__zhQ2Gs?t=10
520
521 binary_node<int> root{10};
522
524
525 root.insert(8, 15, 7, 9, 12, 17, 6, 16, 18, 25);
526
527 if(auto ptr = root.find(6))
528 {
529 stream << "Value found: " << ptr->get() << endl;
530 }
531 else
532 {
533 stream <<"Value not found " << endl;
534 }
535
536 if(auto ptr = root.find(20) )
537 {
538 stream << "Value found: " << ptr->get() << endl;
539 }
540 else
541 {
542 stream << "Value not found " << endl;
543 }
544}
545
546
548{
549 // In courtesy of Vivekanand Khyade - Algorithm Every Day
550 // Introduction to AVL tree (Why AVL tree is needed?)
551 // https://youtu.be/5Q-__zhQ2Gs?t=10
552
553 binary_node<int> root{10};
554
556
557 root.insert(8, 15, 7, 9, 12, 17, 6, 16, 18);
558
559 stream << root.build_digraph() << endl;
560
561}
562
563void print_usage(string_t appname, const char* msg)
564{
565 stream << "Message: " << msg << endL;
566
567 stream << "Usage 1: " << appname << " graph node_list " << endL;
568 stream << "\tExample: " << appname <<" graph 10 8 15 7 9 12 17 6 16 18 > binary_tree.gv" << endL;
569 stream << "\tdot -Tpng binary_tree.gv -o binary_tree.png" << endL;
570
571 stream << "Usage 2: " << appname << " list node_list " << endL;
572 stream << "\tExample: " << appname <<" list 10 8 15 7 9 12 17 6 16 18" << endL;
573
574 stream << "Usage 3: " << appname << " {ascending|desecnding} {successor_of|predecessor_of|exact_match_of} value node_list " << endL;
575 stream << "\tExample: " << appname <<" ascending successor_of 19 10 8 15 7 9 12 17 6 16 18" << endL;
576 stream << "\t\t" <<" Finds the successor of 19 in ascending order from the list 10 8 15 7 9 12 17 6 16 18" << endL;
577
578 stream << "Usage 4: " << appname << " remove value node_list " << endL;
579 stream << "\tExample: " << appname <<" remove 19 10 8 15 7 9 12 17 6 16 18" << endL;
580 stream << "\t\t" <<" Find and remove node with value 19 from the list 10 8 15 7 9 12 17 6 16 18" << endL;
581}
582
583int main(int argc, const char* argv[])
584{
585 // test_ascending_descending_order();
586 // test_find_binary_tree();
587 // test_build_digraph();
588
589 string_t appname{argv[0]};
590
591 if(argc < 2)
592 {
593 print_usage(appname, "Program commandline syntax");
594
595 return 0;
596 }
597
598 string_t command{argv[1]};
599
600 if(command == "graph")
601 {
602 binary_node<int> root { std::atoi(argv[2])};
603
604 for(int i = 3; i < argc; ++i)
605 {
606 root.insert( std::atoi(argv[i]) );
607 }
608
609 stream << root.build_digraph() << endl;
610 }
611 else if(command == "list")
612 {
613 binary_node<int> root { std::atoi(argv[2])};
615
616 for(int i = 3; i < argc; ++i)
617 {
618 root.insert( std::atoi(argv[i]) );
619 }
620
621 stream <<"Ascending order: ";
622 root.visit_nodes(stream, visit_mode::ascending_order);
623 stream << endl;
624
625 stream <<"Descending order: ";
626 root.visit_nodes(stream, visit_mode::descending_order);
627 stream << endl;
628 }
629 else if(command == "ascending"
630 || command == "descending")
631 {
633 using find_mode = binary_node<int>::find_mode;
634
635 visit_mode vmode {visit_mode::undefined};
636 find_mode fmode {find_mode::undefined};
637
638 if(command == "ascending")
639 vmode = visit_mode::ascending_order;
640
641 if(command == "descending")
642 vmode = visit_mode::descending_order;
643
644 if(vmode==visit_mode::undefined)
645 {
646 print_usage(appname, "Undefined visit mode");
647 return 0;
648 }
649
650 if(argc < 5)
651 {
652 print_usage(appname, "Syntax Error");
653 return 0;
654 }
655
656 string_t operation{argv[2]};
657
658 if(operation == "successor_of")
659 fmode = find_mode::successor;
660
661 if(operation == "predecessor_of")
662 fmode = find_mode::predecessor;
663
664 if(operation == "exact_match_of")
665 fmode = find_mode::exact_match;
666
667 if(fmode == find_mode::undefined)
668 {
669 print_usage(appname, "Undefined find mode");
670 return 0;
671 }
672
673 int value = std::atoi(argv[3]);
674
675 binary_node<int> root { std::atoi(argv[4])};
677
678 for(int i = 5; i < argc; ++i)
679 {
680 root.insert( std::atoi(argv[i]) );
681 }
682
683 stream <<"Ascending order: ";
684 root.visit_nodes(stream, visit_mode::ascending_order);
685 stream << endl;
686
687 stream <<"Descending order: ";
688 root.visit_nodes(stream, visit_mode::descending_order);
689 stream << endl;
690
691 auto ptr = root.find(value, fmode, vmode);
692
693 if(ptr)
694 {
695 if(fmode == find_mode::successor)
696 stream << "The successor of " << value;
697 else if (fmode == find_mode::predecessor)
698 stream << "The predecessor of " << value;
699 else if (fmode == find_mode::exact_match)
700 stream << "The exact match of " << value;
701
702 if(vmode == visit_mode::ascending_order)
703 stream << " in ascending order is ";
704
705 if(vmode == visit_mode::descending_order)
706 stream << " in descending order is ";
707
708 stream << ptr->get() << endL;
709 }
710 else
711 {
712 if(fmode == find_mode::successor)
713 stream << "The successor of " << value;
714 else if (fmode == find_mode::predecessor)
715 stream << "The predecessor of " << value;
716 else if (fmode == find_mode::exact_match)
717 stream << "The exact match of " << value;
718
719 if(vmode == visit_mode::ascending_order)
720 stream << " in ascending order is NOT found";
721
722 if(vmode == visit_mode::descending_order)
723 stream << " in descending order NOT found ";
724
725 stream << endL;
726 }
727 }
728 else if(command == "remove")
729 {
730 using binary_node_t = binary_node<int>;
731 using node_ptr_t = std::unique_ptr<binary_node_t>;
732
733 // binary_tree remove 10 10
734 if(argc < 4)
735 {
736 print_usage(appname, "Syntax Error");
737 return 0;
738 }
739
740 int value = std::atoi(argv[2]);
741
742 node_ptr_t root = std::make_unique<binary_node_t>(std::atoi(argv[3]));
743
744 for(int i = 4; i < argc; ++i)
745 {
746 root->insert( std::atoi(argv[i]) );
747 }
748
749 test_remove_node(root, value);
750 }
751}
std::string string_t
std::enable_if_t< tpf::types::is_same_v< tpf::remove_cv_ref_t< Types >... >, ReturnType > enable_if_all_types_are_the_same_t
auto endL
tpf::sstream stream
auto nL
void test_remove_node(node_ptr_t< ElementType > &node_ptr, ElementType value)
void print_usage(string_t appname, const char *msg)
auto endl
void test_ascending_descending_order()
void test_find_binary_tree()
int main(int argc, const char *argv[])
typename binary_node< ElementType >::node_ptr_t node_ptr_t
auto nl
void test_build_digraph()
typename graph_t::visit_mode visit_mode
Definition: 060-graph02.cpp:8
static bool remove_node(node_ptr_t &root_ptr, ElementType value)
binary_node * find(ElementType value)
enable_if_all_types_are_the_same_t< bool, ElementType, Type, Types... > insert(Type &&arg, Types &&... args)
child_status get_child_status(binary_node *child)
tpf::sstream & get_node_name(tpf::sstream &os)
bool graft(node_ptr_t node_ptr)
string_t build_digraph()
const ElementType & get() const
void print_node(tpf::sstream &os)
std::unique_ptr< binary_node > node_ptr_t
tpf::sstream & get_node_definition(tpf::sstream &os)
bool insert(ElementType value)
enable_if_all_types_are_the_same_t< bool, ElementType, Type > insert(Type &&value)
binary_node * find(ElementType value, find_mode fmode, visit_mode vmode=visit_mode::ascending_order)
binary_node(ElementType value=ElementType{}, binary_node *parent=nullptr)
int height(bool bRecalculate=false) const
void visit_nodes(tpf::sstream &os, visit_mode order=visit_mode::in_order)
std::string str() const
Definition: tpf_output.hpp:951
string_stream & clear()
Definition: tpf_output.hpp:916
constexpr auto endL
Definition: tpf_output.hpp:974
constexpr auto nL
Definition: tpf_output.hpp:972
constexpr auto endl
Definition: tpf_output.hpp:973
constexpr auto nl
Definition: tpf_output.hpp:971
Stream output operators << are implemented.