C++ Library Extensions 2022.12.09
To help learn modern C++ programming
054-binary_tree06.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 = \"m_value: "
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 // node_ptr_t == std::unique_ptr<binary_node>
238 bool graft(node_ptr_t& node_ptr)
239 {
240 if(node_ptr->m_value < this->m_value)
241 {
242 if(this->m_left)
243 // this is recursion
244 return this->m_left->graft(node_ptr);
245 else
246 {
247 node_ptr->m_parent = this;
248 this->m_left = std::move(node_ptr);
249 this->m_left->update_height();
250
251 return true;
252 }
253 }
254 else // node_ptr->m_value > this->m_value
255 {
256 if(this->m_right)
257 // this is also recursion
258 return this->m_right->graft(node_ptr);
259 else
260 {
261 node_ptr->m_parent = this;
262 this->m_right = std::move(node_ptr);
263 this->m_right->update_height();
264
265 return true;
266 }
267 }
268 }
269
270 // return true only if all parameters are inserted
271 // successfully, otherwise returns false
272 template<typename Type, typename... Types>
273 // we enable this function only when ElementType, Type, and Types... are
274 // of same type
275 enable_if_all_types_are_the_same_t<bool, ElementType, Type, Types...>
276 insert(Type&& arg, Types&&... args)
277 {
278 bool result = this->insert(std::forward<Type>(arg));
279
280 if constexpr(sizeof...(args) != 0)
281 {
282 // recursion
283 return result && this->insert(std::forward<Types>(args)...);
284 }
285 else
286 return result;
287 }
288
289 // for more information about visiting order
290 // Binary Tree (ALL Interview Questions)
291 // https://www.youtube.com/watch?v=VQTF_pRTZek&list=PLeIMaH7i8JDj7DnmO7lll97P1yZjMCpgY&index=2
293 {
294 switch(order)
295 {
297 {
298 os << this->m_value << ", ";
299
300 if(this->m_left)
301 this->m_left->visit_nodes(os, order);
302
303 if(this->m_right)
304 this->m_right->visit_nodes(os, order);
305
306 return;
307 }
309 {
310 if(this->m_left)
311 this->m_left->visit_nodes(os, order);
312
313 if(this->m_right)
314 this->m_right->visit_nodes(os, order);
315
316 os << this->m_value << ", ";
317
318 return;
319 }
320
322 {
323 if(this->m_right)
324 this->m_right->visit_nodes(os, order);
325
326 os << this->m_value << ", ";
327
328 if(this->m_left)
329 this->m_left->visit_nodes(os, order);
330
331 return;
332 }
333
335 default: // in_order
336 {
337 if(this->m_left)
338 this->m_left->visit_nodes(os, order);
339
340 os << this->m_value << ", ";
341
342 if(this->m_right)
343 this->m_right->visit_nodes(os, order);
344
345 return;
346 }
347 }
348 }
349
350 // if fails, return nullptr
351 binary_node* find(ElementType value, find_mode fmode,
353 {
354 binary_node* ptr = nullptr;
355
356 if(((vmode==visit_mode::ascending_order) && (fmode==find_mode::successor))||
358 {
359 if(this->m_left && (ptr = this->m_left->find(value, fmode, vmode)))
360 return ptr;
361
362 if( value < this->m_value)
363 return this;
364
365 if(this->m_right && (ptr = this->m_right->find(value, fmode, vmode)))
366 return ptr;
367
368 // we failed
369 return ptr;
370 }
371 else if(( (vmode == visit_mode::ascending_order)&&(fmode==find_mode::predecessor)) ||
373 {
374 if(this->m_right && (ptr = this->m_right->find(value, fmode, vmode)))
375 return ptr;
376
377 if(value > this->m_value)
378 return this;
379
380 if(this->m_left && (ptr = this->m_left->find(value, fmode, vmode)))
381 return ptr;
382
383 return ptr;
384 }
385 else if(fmode == find_mode::exact_match)
386 return this->find(value);
387 else
388 return nullptr;
389 }
390
392 {
393 // both m_left and m_right are nullptr
394 return (!this->m_left && !this->m_right);
395 }
396
398 {
399 if(this->m_left && (ptr == this->m_left.get()))
400 return std::move(this->m_left);
401 else if(this->m_right && (ptr == this->m_right.get()))
402 return std::move(this->m_right);
403 else
404 return nullptr;
405 }
406
407 static bool remove_node(node_ptr_t& root_ptr, ElementType value)
408 {
409 binary_node* ptr = root_ptr->find(value);
410
411 // node with "value" does not exist
412 if(!ptr) return false;
413
414 // if parent is nullptr, then it is root node
415 if(!ptr->m_parent)
416 {
417 // ptr is a root node of type binary_node*
418 // root_ptr is a root node of type
419 // std::unique_ptr<binary_node>;
420
421 // we move left child to left_child
422 // after move, root_ptr->m_left is invalid
423 node_ptr_t left_child
424 = std::move(root_ptr->m_left);
425
426 // after move, root_ptr->m_right is invalid
427 node_ptr_t right_child
428 = std::move(root_ptr->m_right);
429
430 if(right_child) // new root
431 {
432 // because root node does not have its parent.
433 right_child->m_parent = nullptr;
434
435 // existing root_ptr is destroyed
436 // and right_child is moved to root_ptr
437 // at this point, existing old root is
438 // properly released.
439 root_ptr = std::move(right_child);
440
441 // do not forget this part
442 if(left_child)
443 root_ptr->graft(left_child);
444
445 return true;
446 }
447 else if(left_child) // right_child is nullptr
448 // left_child is a new root.
449 {
450 // because root node does not have its parent.
451 left_child->m_parent = nullptr;
452
453 // after move, the older root is released
454 // and left_child is moved to root_ptr
455 root_ptr = std::move(left_child);
456
457 return true;
458 }
459 else
460 {
461 // since we destroyed the root
462 // we should not access the root after this statment
463 root_ptr.reset();
464
465 return true;
466 }
467 }
468 else // non-root node
469 {
470 // ptr is the node to remove
471 // is not a root node.
472
473 if(ptr->is_leaf_node())
474 {
475 // this code cuts off the relation
476 // between the child node and its parent.
477 node_ptr_t orphan =
478 ptr->m_parent->release_child(ptr);
479
480 // when the orphan goes off this block,
481 // the allocated memory for orphan is
482 // cleaned up. and orphan.get() == ptr
483
484 if(ptr->m_parent->is_leaf_node())
485 {
486 ptr->m_parent->m_height = 1;
487 ptr->m_parent->update_height();
488 }
489
490 return true;
491 }
492 else // not a leaf node,
493 {
494 // cut off the relationship between ptr
495 // and its parent.
496 node_ptr_t orphan =
497 ptr->m_parent->release_child(ptr);
498
499 // this is for update the height of the parents
500 // nodes
501 ptr->m_parent->height(true);
502 ptr->m_parent->update_height();
503
504 node_ptr_t left_child =
505 std::move(orphan->m_left);
506
507 node_ptr_t right_child =
508 std::move(orphan->m_right);
509
510 if(right_child)
511 root_ptr->graft(right_child);
512
513 if(left_child)
514 root_ptr->graft(left_child);
515
516 return true;
517 }
518 }
519 }
520}; // end of binary_node class
521
522// std::unique_ptr<binary_node<ElementType>>
523template<typename ElementType>
525
526template<typename ElementType>
527void test_remove_node(node_ptr_t<ElementType>& node_ptr, ElementType value)
528{
529 using binary_node_t = binary_node<ElementType>;
530 using node_ptr_t = std::unique_ptr<binary_node_t>;
531
532 binary_node_t::remove_node(node_ptr, value);
533
534 if(node_ptr)
535 {
536 stream << node_ptr->build_digraph() << endl;
537 }
538
539}
540
542{
543 // In courtesy of Vivekanand Khyade - Algorithm Every Day
544 // Introduction to AVL tree (Why AVL tree is needed?)
545 // https://youtu.be/5Q-__zhQ2Gs?t=10
546
547 binary_node<int> root{10};
548
550
551 root.insert(8);
552 root.insert(15);
553
554 root.insert(7);
555 root.insert(9);
556 root.insert(12);
557 root.insert(17);
558
559 root.insert(6);
560 root.insert(16);
561 root.insert(18);
562
563 tpf::sstream os;
564
565 root.visit_nodes(os, visit_mode::ascending_order);
566 stream << "Ascending-order: " << endL;
567 stream << os.str() << endL;
568
569 os.clear();
570
571 root.visit_nodes(os, visit_mode::descending_order);
572 stream << "Descending-order: " << endL;
573 stream << os.str() << endL;
574}
575
577{
578 // In courtesy of Vivekanand Khyade - Algorithm Every Day
579 // Introduction to AVL tree (Why AVL tree is needed?)
580 // https://youtu.be/5Q-__zhQ2Gs?t=10
581
582 binary_node<int> root{10};
583
585
586 root.insert(8, 15, 7, 9, 12, 17, 6, 16, 18, 25);
587
588 if(auto ptr = root.find(6))
589 {
590 stream << "Value found: " << ptr->get() << endl;
591 }
592 else
593 {
594 stream <<"Value not found " << endl;
595 }
596
597 if(auto ptr = root.find(20) )
598 {
599 stream << "Value found: " << ptr->get() << endl;
600 }
601 else
602 {
603 stream << "Value not found " << endl;
604 }
605}
606
607
609{
610 // In courtesy of Vivekanand Khyade - Algorithm Every Day
611 // Introduction to AVL tree (Why AVL tree is needed?)
612 // https://youtu.be/5Q-__zhQ2Gs?t=10
613
614 binary_node<int> root{10};
615
617
618 root.insert(8, 15, 7, 9, 12, 17, 6, 16, 18);
619
620 stream << root.build_digraph() << endl;
621
622}
623
624void print_usage(string_t appname, const char* msg)
625{
626 stream << "Message: " << msg << endL;
627
628 stream << "Usage 1: " << appname << " graph node_list " << endL;
629 stream << "\tExample: " << appname <<" graph 10 8 15 7 9 12 17 6 16 18 > binary_tree.gv" << endL;
630 stream << "\tdot -Tpng binary_tree.gv -o binary_tree.png" << endL;
631
632 stream << "Usage 2: " << appname << " list node_list " << endL;
633 stream << "\tExample: " << appname <<" list 10 8 15 7 9 12 17 6 16 18" << endL;
634
635 stream << "Usage 3: " << appname << " {ascending|desecnding} {successor_of|predecessor_of|exact_match_of} value node_list " << endL;
636 stream << "\tExample: " << appname <<" ascending successor_of 19 10 8 15 7 9 12 17 6 16 18" << endL;
637 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;
638
639 stream << "Usage 4: " << appname << " remove value node_list " << endL;
640 stream << "\tExample: " << appname <<" remove 19 10 8 15 7 9 12 17 6 16 18" << endL;
641 stream << "\t\t" <<" Find and remove node with value 19 from the list 10 8 15 7 9 12 17 6 16 18" << endL;
642}
643
644int main(int argc, const char* argv[])
645{
646 // test_ascending_descending_order();
647 // test_find_binary_tree();
648 // test_build_digraph();
649
650 string_t appname{argv[0]};
651
652 if(argc < 2)
653 {
654 print_usage(appname, "Program commandline syntax");
655
656 return 0;
657 }
658
659 string_t command{argv[1]};
660
661 if(command == "graph")
662 {
663 binary_node<int> root { std::atoi(argv[2])};
664
665 for(int i = 3; i < argc; ++i)
666 {
667 root.insert( std::atoi(argv[i]) );
668 }
669
670 stream << root.build_digraph() << endl;
671 }
672 else if(command == "list")
673 {
674 binary_node<int> root { std::atoi(argv[2])};
676
677 for(int i = 3; i < argc; ++i)
678 {
679 root.insert( std::atoi(argv[i]) );
680 }
681
682 stream <<"Ascending order: ";
683 root.visit_nodes(stream, visit_mode::ascending_order);
684 stream << endl;
685
686 stream <<"Descending order: ";
687 root.visit_nodes(stream, visit_mode::descending_order);
688 stream << endl;
689 }
690 else if(command == "ascending"
691 || command == "descending")
692 {
694 using find_mode = binary_node<int>::find_mode;
695
696 visit_mode vmode {visit_mode::undefined};
697 find_mode fmode {find_mode::undefined};
698
699 if(command == "ascending")
700 vmode = visit_mode::ascending_order;
701
702 if(command == "descending")
703 vmode = visit_mode::descending_order;
704
705 if(vmode==visit_mode::undefined)
706 {
707 print_usage(appname, "Undefined visit mode");
708 return 0;
709 }
710
711 if(argc < 5)
712 {
713 print_usage(appname, "Syntax Error");
714 return 0;
715 }
716
717 string_t operation{argv[2]};
718
719 if(operation == "successor_of")
720 fmode = find_mode::successor;
721
722 if(operation == "predecessor_of")
723 fmode = find_mode::predecessor;
724
725 if(operation == "exact_match_of")
726 fmode = find_mode::exact_match;
727
728 if(fmode == find_mode::undefined)
729 {
730 print_usage(appname, "Undefined find mode");
731 return 0;
732 }
733
734 int value = std::atoi(argv[3]);
735
736 binary_node<int> root { std::atoi(argv[4])};
738
739 for(int i = 5; i < argc; ++i)
740 {
741 root.insert( std::atoi(argv[i]) );
742 }
743
744 stream <<"Ascending order: ";
745 root.visit_nodes(stream, visit_mode::ascending_order);
746 stream << endl;
747
748 stream <<"Descending order: ";
749 root.visit_nodes(stream, visit_mode::descending_order);
750 stream << endl;
751
752 auto ptr = root.find(value, fmode, vmode);
753
754 if(ptr)
755 {
756 if(fmode == find_mode::successor)
757 stream << "The successor of " << value;
758 else if (fmode == find_mode::predecessor)
759 stream << "The predecessor of " << value;
760 else if (fmode == find_mode::exact_match)
761 stream << "The exact match of " << value;
762
763 if(vmode == visit_mode::ascending_order)
764 stream << " in ascending order is ";
765
766 if(vmode == visit_mode::descending_order)
767 stream << " in descending order is ";
768
769 stream << ptr->get() << endL;
770 }
771 else
772 {
773 if(fmode == find_mode::successor)
774 stream << "The successor of " << value;
775 else if (fmode == find_mode::predecessor)
776 stream << "The predecessor of " << value;
777 else if (fmode == find_mode::exact_match)
778 stream << "The exact match of " << value;
779
780 if(vmode == visit_mode::ascending_order)
781 stream << " in ascending order is NOT found";
782
783 if(vmode == visit_mode::descending_order)
784 stream << " in descending order NOT found ";
785
786 stream << endL;
787 }
788 }
789 else if(command == "remove")
790 {
791 using binary_node_t = binary_node<int>;
792 using node_ptr_t = std::unique_ptr<binary_node_t>;
793
794 // binary_tree remove 10 10
795 if(argc < 4)
796 {
797 print_usage(appname, "Syntax Error");
798 return 0;
799 }
800
801 int value = std::atoi(argv[2]);
802
803 node_ptr_t root = std::make_unique<binary_node_t>(std::atoi(argv[3]));
804
805 for(int i = 4; i < argc; ++i)
806 {
807 root->insert( std::atoi(argv[i]) );
808 }
809
810 test_remove_node(root, value);
811 }
812}
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
typename binary_node< ElementType >::node_ptr_t node_ptr_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[])
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)
ElementType get() const
string_t build_digraph()
const ElementType & get() const
node_ptr_t release_child(binary_node *ptr)
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.