C++ Library Extensions 2022.12.09
To help learn modern C++ programming
052-binary_tree04.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 // return true only if all parameters are inserted
238 // successfully, otherwise returns false
239 template<typename Type, typename... Types>
240 // we enable this function only when ElementType, Type, and Types... are
241 // of same type
242 enable_if_all_types_are_the_same_t<bool, ElementType, Type, Types...>
243 insert(Type&& arg, Types&&... args)
244 {
245 bool result = this->insert(std::forward<Type>(arg));
246
247 if constexpr(sizeof...(args) != 0)
248 {
249 // recursion
250 return result && this->insert(std::forward<Types>(args)...);
251 }
252 else
253 return result;
254 }
255
256 // for more information about visiting order
257 // Binary Tree (ALL Interview Questions)
258 // https://www.youtube.com/watch?v=VQTF_pRTZek&list=PLeIMaH7i8JDj7DnmO7lll97P1yZjMCpgY&index=2
260 {
261 switch(order)
262 {
264 {
265 os << this->m_value << ", ";
266
267 if(this->m_left)
268 this->m_left->visit_nodes(os, order);
269
270 if(this->m_right)
271 this->m_right->visit_nodes(os, order);
272
273 return;
274 }
276 {
277 if(this->m_left)
278 this->m_left->visit_nodes(os, order);
279
280 if(this->m_right)
281 this->m_right->visit_nodes(os, order);
282
283 os << this->m_value << ", ";
284
285 return;
286 }
287
289 {
290 if(this->m_right)
291 this->m_right->visit_nodes(os, order);
292
293 os << this->m_value << ", ";
294
295 if(this->m_left)
296 this->m_left->visit_nodes(os, order);
297
298 return;
299 }
300
302 default: // in_order
303 {
304 if(this->m_left)
305 this->m_left->visit_nodes(os, order);
306
307 os << this->m_value << ", ";
308
309 if(this->m_right)
310 this->m_right->visit_nodes(os, order);
311
312 return;
313 }
314 }
315 }
316
317 // if fails, return nullptr
318 binary_node* find(ElementType value, find_mode fmode,
320 {
321 binary_node* ptr = nullptr;
322
323 if(((vmode==visit_mode::ascending_order) && (fmode==find_mode::successor))||
325 {
326 if(this->m_left && (ptr = this->m_left->find(value, fmode, vmode)))
327 return ptr;
328
329 if( value < this->m_value)
330 return this;
331
332 if(this->m_right && (ptr = this->m_right->find(value, fmode, vmode)))
333 return ptr;
334
335 // we failed
336 return ptr;
337 }
338 else if(( (vmode == visit_mode::ascending_order)&&(fmode==find_mode::predecessor)) ||
340 {
341 if(this->m_right && (ptr = this->m_right->find(value, fmode, vmode)))
342 return ptr;
343
344 if(value > this->m_value)
345 return this;
346
347 if(this->m_left && (ptr = this->m_left->find(value, fmode, vmode)))
348 return ptr;
349
350 return ptr;
351 }
352 else if(fmode == find_mode::exact_match)
353 return this->find(value);
354 else
355 return nullptr;
356 }
357};
358
360{
361 // In courtesy of Vivekanand Khyade - Algorithm Every Day
362 // Introduction to AVL tree (Why AVL tree is needed?)
363 // https://youtu.be/5Q-__zhQ2Gs?t=10
364
365 binary_node<int> root{10};
366
368
369 root.insert(8);
370 root.insert(15);
371
372 root.insert(7);
373 root.insert(9);
374 root.insert(12);
375 root.insert(17);
376
377 root.insert(6);
378 root.insert(16);
379 root.insert(18);
380
381 tpf::sstream os;
382
383 root.visit_nodes(os, visit_mode::ascending_order);
384 stream << "Ascending-order: " << endL;
385 stream << os.str() << endL;
386
387 os.clear();
388
389 root.visit_nodes(os, visit_mode::descending_order);
390 stream << "Descending-order: " << endL;
391 stream << os.str() << endL;
392}
393
395{
396 // In courtesy of Vivekanand Khyade - Algorithm Every Day
397 // Introduction to AVL tree (Why AVL tree is needed?)
398 // https://youtu.be/5Q-__zhQ2Gs?t=10
399
400 binary_node<int> root{10};
401
403
404 root.insert(8, 15, 7, 9, 12, 17, 6, 16, 18, 25);
405
406 if(auto ptr = root.find(6))
407 {
408 stream << "Value found: " << ptr->get() << endl;
409 }
410 else
411 {
412 stream <<"Value not found " << endl;
413 }
414
415 if(auto ptr = root.find(20) )
416 {
417 stream << "Value found: " << ptr->get() << endl;
418 }
419 else
420 {
421 stream << "Value not found " << endl;
422 }
423}
424
425
427{
428 // In courtesy of Vivekanand Khyade - Algorithm Every Day
429 // Introduction to AVL tree (Why AVL tree is needed?)
430 // https://youtu.be/5Q-__zhQ2Gs?t=10
431
432 binary_node<int> root{10};
433
435
436 root.insert(8, 15, 7, 9, 12, 17, 6, 16, 18);
437
438 stream << root.build_digraph() << endl;
439
440}
441
442void print_usage(const char* appname, const char* msg)
443{
444 stream << "Message: " << msg << endL;
445
446 stream << "Usage 1: " << appname << " graph node_list " << endL;
447 stream << "\tExample: " << appname <<" graph 10 8 15 7 9 12 17 6 16 18 > binary_tree.gv" << endL;
448 stream << "\tdot -Tpng binary_tree.gv -o binary_tree.png" << endL;
449
450 stream << "Usage 2: " << appname << " list node_list " << endL;
451 stream << "\tExample: " << appname <<" list 10 8 15 7 9 12 17 6 16 18" << endL;
452
453 stream << "Usage 3: " << appname << " {ascending|desecnding} {successor_of|predecessor_of|exact_match_of} value node_list " << endL;
454 stream << "\tExample: " << appname <<" ascending successor_of 19 10 8 15 7 9 12 17 6 16 18" << endL;
455 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;
456}
457
458int main(int argc, const char* argv[])
459{
460 // test_ascending_descending_order();
461 // test_find_binary_tree();
462 // test_build_digraph();
463
464 if(argc < 2)
465 {
466 print_usage(argv[0], "Program commandline syntax");
467
468 return 0;
469 }
470
471 if(string_t(argv[1]) == "graph")
472 {
473 binary_node<int> root { std::atoi(argv[2])};
474
475 for(int i = 3; i < argc; ++i)
476 {
477 root.insert( std::atoi(argv[i]) );
478 }
479
480 stream << root.build_digraph() << endl;
481 }
482 else if(string_t(argv[1]) == "list")
483 {
484 binary_node<int> root { std::atoi(argv[2])};
486
487 for(int i = 3; i < argc; ++i)
488 {
489 root.insert( std::atoi(argv[i]) );
490 }
491
492 stream <<"Ascending order: ";
493 root.visit_nodes(stream, visit_mode::ascending_order);
494 stream << endl;
495
496 stream <<"Descending order: ";
497 root.visit_nodes(stream, visit_mode::descending_order);
498 stream << endl;
499 }
500 else if(string_t(argv[1]) == "ascending"
501 || string_t(argv[1]) == "descending")
502 {
504 using find_mode = binary_node<int>::find_mode;
505
506 visit_mode vmode {visit_mode::undefined};
507 find_mode fmode {find_mode::undefined};
508
509 if(string_t(argv[1]) == "ascending")
510 vmode = visit_mode::ascending_order;
511
512 if(string_t(argv[1]) == "descending")
513 vmode = visit_mode::descending_order;
514
515 if(vmode==visit_mode::undefined)
516 {
517 print_usage(argv[0], "Undefined visit mode");
518 return 0;
519 }
520
521 if(string_t(argv[2]) == "successor_of")
522 fmode = find_mode::successor;
523
524 if(string_t(argv[2]) == "predecessor_of")
525 fmode = find_mode::predecessor;
526
527 if(string_t(argv[2]) == "exact_match_of")
528 fmode = find_mode::exact_match;
529
530 if(fmode == find_mode::undefined)
531 {
532 print_usage(argv[0], "Undefined find mode");
533 return 0;
534 }
535
536 int value = std::atoi(argv[3]);
537
538 binary_node<int> root { std::atoi(argv[4])};
540
541 for(int i = 5; i < argc; ++i)
542 {
543 root.insert( std::atoi(argv[i]) );
544 }
545
546 stream <<"Ascending order: ";
547 root.visit_nodes(stream, visit_mode::ascending_order);
548 stream << endl;
549
550 stream <<"Descending order: ";
551 root.visit_nodes(stream, visit_mode::descending_order);
552 stream << endl;
553
554 if(fmode == find_mode::undefined || vmode == visit_mode::undefined)
555 {
556 print_usage(argv[0], "Syntax error!");
557 return 0;
558 }
559
560 auto ptr = root.find(value, fmode, vmode);
561
562 if(ptr)
563 {
564 if(fmode == find_mode::successor)
565 stream << "The successor of " << value;
566 else if (fmode == find_mode::predecessor)
567 stream << "The predecessor of " << value;
568 else if (fmode == find_mode::exact_match)
569 stream << "The exact match of " << value;
570
571 if(vmode == visit_mode::ascending_order)
572 stream << " in ascending order is ";
573
574 if(vmode == visit_mode::descending_order)
575 stream << " in descending order is ";
576
577 stream << ptr->get() << endL;
578 }
579 else
580 {
581 if(fmode == find_mode::successor)
582 stream << "The successor of " << value;
583 else if (fmode == find_mode::predecessor)
584 stream << "The predecessor of " << value;
585 else if (fmode == find_mode::exact_match)
586 stream << "The exact match of " << value;
587
588 if(vmode == visit_mode::ascending_order)
589 stream << " in ascending order is NOT found";
590
591 if(vmode == visit_mode::descending_order)
592 stream << " in descending order NOT found ";
593
594 stream << endL;
595 }
596
597 }
598}
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
std::string string_t
void print_usage(const char *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 binary_node< ElementType >::node_ptr_t node_ptr_t
typename graph_t::visit_mode visit_mode
Definition: 060-graph02.cpp:8
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)
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.