A few months ago, I was on Wikipedia1 doing research on AVL trees. It stated that red-black trees beat AVL trees when manipulating the data a lot, but AVL trees are better for look-up-intensive operations. Okay, it may have had a "[citation needed]" out to the side, but I assumed that they knew what they were talking about. Then I compared my final AVL tree to std::set (a red-black tree in my case), and my AVL tree beat the socks off of it! I figured it had to do to the fact that I was inserting the data in order. Maybe red-black trees are better when inserting data in random order. I had to test this, so I spent the next month studying red-black trees, and then it came time for coding one and testing a few things.
Hypothesis:
AVL trees beat red-black trees when searching and when inserting or erasing data in order, but red-black trees beat AVL trees when inserting or erasing data randomly. This is my hypothesis.
Test:
The code below is used to test the speed of red-black trees compared to speed of AVL trees. It tests the trees on nine different amounts of data. It is in C++11, and I suggest that it be compiled with level two optimization.
The AVL tree is based off of code I made a few months ago, and the red-black tree is based off of the code in my red-black tree tutorial2.
1 //
2 // AVL versus Red-Black
3 // written by Nathan Belue
4 // April 30, 2012 - May 1, 2012
5 //
6 // This is a comparison of AVL trees to red-black trees. It
7 // tests various scenarios and the speed of each tree in each
8 // scenario.
9 //
10
11
12 /*
13 * Included files
14 */
15
16 #include <chrono>
17 #include <cstdlib>
18 #include <iostream>
19
20 using namespace std;
21
22
23
24 /*
25 * Constants
26 */
27
28
29 namespace {
30
31 //
32 // Set this to how big of intervals to step
33 //
34 const int INTERVAL = 100000;
35
36
37 //
38 // The square root of the maximum random value
39 //
40 const int SQRT_RAND_MAX = 100000;
41
42 }
43
44
45
46 /*
47 * Binary search tree base
48 */
49
50
51 //
52 // Value being stored
53 //
54 typedef int value_type;
55 typedef const value_type & const_reference;
56
57
58 //
59 // More typedefs
60 //
61 typedef size_t size_type;
62
63
64 //
65 // Colors
66 //
67 enum color_type
68 {
69 RED,
70 BLACK
71 };
72
73
74 //
75 // A node of a tree
76 //
77 struct tree_node
78 {
79 value_type data;
80 tree_node * parent;
81 tree_node * left;
82 tree_node * right;
83 union {
84 color_type color;
85 size_type height;
86 };
87 };
88 typedef tree_node * node_ptr;
89 typedef node_ptr * link_type;
90
91
92 //
93 // Binary search tree base class
94 //
95 class binary_search_tree_base
96 {
97 protected:
98 // Data
99 node_ptr _root;
100
101 private:
102 // bst helper functions
103 void _clear( node_ptr );
104
105 protected:
106 // Links
107 typedef node_ptr * node_link;
108 link_type _make_link( node_ptr & );
109 link_type _get_parent_link( node_ptr );
110 node_ptr _link_dest( node_link );
111 void _link_set_dest( node_link, node_ptr );
112 node_ptr _link_origin( node_link );
113
114 protected:
115 // Node functions
116 color_type _node_color( node_ptr );
117 size_type _node_height( node_ptr );
118 node_ptr _node_sibling( node_ptr );
119 node_ptr _node_leftmost( node_ptr );
120
121 protected:
122 // Helper functions
123 void _rotate_clockwise( link_type node );
124 void _rotate_counterclockwise( link_type node );
125 pair<link_type, node_ptr> _get_insert_link(
126 const_reference );
127 node_ptr _replace_and_remove_node( node_ptr );
128
129 public:
130 // Interface
131 binary_search_tree_base();
132 ~binary_search_tree_base();
133 void clear();
134 node_ptr find( const_reference );
135 bool is_empty();
136 };
137
138
139 // Recursively clear the tree
140 void
141 binary_search_tree_base::_clear(
142 node_ptr node )
143 {
144 if( node ) {
145 _clear( node->left );
146 _clear( node->right );
147 delete node;
148 }
149 }
150
151
152 // Convert a node pointer to a link
153 link_type
154 binary_search_tree_base::_make_link(
155 node_ptr & node )
156 {
157 return & node;
158 }
159
160 // Get a node's parent's link to that node
161 link_type
162 binary_search_tree_base::_get_parent_link(
163 node_ptr node )
164 {
165 node_ptr parent = node->parent;
166 if( ! parent )
167 return _make_link( _root );
168 if( parent->left == node )
169 return _make_link( parent->left );
170 return _make_link( parent->right );
171 }
172
173 // Get a link's destination
174 node_ptr
175 binary_search_tree_base::_link_dest(
176 link_type link )
177 {
178 return *link;
179 }
180
181 // Set a link's destination
182 void
183 binary_search_tree_base::_link_set_dest(
184 link_type link, node_ptr dest )
185 {
186 *link = dest;
187 }
188
189 // Get a link's origin
190 node_ptr
191 binary_search_tree_base::_link_origin(
192 node_link link )
193 {
194 return _link_dest(link) -> parent;
195 }
196
197
198 // Get a node's color
199 // Also works for nonexistent imaginary black leaf nodes
200 color_type
201 binary_search_tree_base::_node_color(
202 node_ptr node )
203 {
204 if( node )
205 return node->color;
206 return BLACK;
207 }
208
209 // Get a node's height
210 size_type
211 binary_search_tree_base::_node_height(
212 node_ptr node )
213 {
214 if( node )
215 return node->height;
216 return 0;
217 }
218
219 // Get a node's sibling
220 node_ptr
221 binary_search_tree_base::_node_sibling(
222 node_ptr node )
223 {
224 node_ptr par = node->parent;
225 node_ptr sib = par->left;
226 if( sib == node )
227 sib = par->right;
228 return sib;
229 }
230
231 // Return the leftmost node of a subtree
232 node_ptr
233 binary_search_tree_base::_node_leftmost(
234 node_ptr node )
235 {
236 while( node->left )
237 node = node->left;
238 return node;
239 }
240
241
242 // Rotate about a node clockwise
243 void
244 binary_search_tree_base::_rotate_clockwise(
245 link_type link )
246 {
247 node_ptr node = _link_dest(link);
248 node_ptr left = node->left;
249 node_ptr lright = left->right;
250
251 _link_set_dest( link, left );
252 left->parent = node->parent;
253
254 left->right = node;
255 node->parent = left;
256
257 node->left = lright;
258 if( lright )
259 lright->parent = node;
260 }
261
262 // Rotate about a node counterclockwise
263 void
264 binary_search_tree_base::_rotate_counterclockwise(
265 link_type link )
266 {
267 node_ptr node = _link_dest(link);
268 node_ptr right = node->right;
269 node_ptr rleft = right->left;
270
271 _link_set_dest( link, right );
272 right->parent = node->parent;
273
274 right->left = node;
275 node->parent = right;
276
277 node->right = rleft;
278 if( rleft )
279 rleft->parent = node;
280 }
281
282 // Figure out where to insert a value into the tree
283 // Returns the link and its origin
284 pair<link_type, node_ptr>
285 binary_search_tree_base::_get_insert_link(
286 const_reference value )
287 {
288 link_type where = _make_link(_root);
289 node_ptr origin = nullptr;
290
291 while( _link_dest(where) ) {
292 origin = _link_dest(where);
293 if( value < origin->data )
294 where = _make_link( origin->left );
295 else if( value > origin->data )
296 where = _make_link( origin->right );
297 else {
298 where = nullptr;
299 break;
300 }
301 }
302
303 return make_pair( where, origin );
304 }
305
306 // Replace and remove node
307 // Returns actual node removed
308 node_ptr
309 binary_search_tree_base::_replace_and_remove_node(
310 node_ptr node )
311 {
312 node_ptr rep = nullptr;
313
314 if( node->left && node->right ) {
315 rep = _node_leftmost( node->right );
316 std::swap( rep->data, node->data );
317 node = rep;
318 rep = nullptr;
319 }
320
321 if( node->left )
322 rep = node->left;
323 else if( node->right )
324 rep = node->right;
325
326 _link_set_dest( _get_parent_link(node), rep );
327 if( rep )
328 rep->parent = node->parent;
329
330 return node;
331 }
332
333
334 // Constructor
335 binary_search_tree_base::binary_search_tree_base() :
336 _root( nullptr )
337 {
338 }
339
340 // Destructor
341 binary_search_tree_base::~binary_search_tree_base()
342 {
343 _clear( _root );
344 }
345
346 // Clear the tree
347 void
348 binary_search_tree_base::clear()
349 {
350 _clear( _root );
351 _root = nullptr;
352 }
353
354 // Find a value in the tree
355 node_ptr
356 binary_search_tree_base::find(
357 const_reference value )
358 {
359 node_ptr node = _root;
360 while( node ) {
361 if( value < node->data )
362 node = node->left;
363 else if( value > node->data )
364 node = node->right;
365 else
366 break;
367 }
368 return node;
369 }
370
371 // Is the tree empty?
372 bool
373 binary_search_tree_base::is_empty()
374 {
375 return _root == nullptr;
376 }
377
378
379
380 /*
381 * AVL tree
382 */
383
384
385 //
386 // AVL tree class
387 //
388 class avl_tree : public binary_search_tree_base
389 {
390 protected:
391 // Node functions
392 int _node_balance( node_ptr );
393 protected:
394 // Helper functions
395 bool _update_height( node_ptr );
396 bool _balance_node( node_ptr );
397 void _balance( node_ptr );
398 int _check( node_ptr );
399 public:
400 // Interface functions
401 void insert( const_reference );
402 void erase( const_reference );
403 void check();
404 };
405
406
407 // Balance of a node
408 int avl_tree::_node_balance( node_ptr node )
409 {
410 return _node_height(node->left)
411 - _node_height(node->right);
412 }
413
414
415 // Update the height of anode
416 // Returns true if height was changed
417 bool avl_tree::_update_height( node_ptr node )
418 {
419 if( ! node )
420 return false;
421 size_type oldH = node->height;
422 size_type leftH = _node_height( node->left );
423 size_type rightH = _node_height( node->right );
424 if( leftH > rightH )
425 node->height = leftH + 1;
426 else
427 node->height = rightH + 1;
428 return ! (oldH == node->height);
429 }
430
431 // Balance at node; return true if balance was done
432 bool avl_tree::_balance_node( node_ptr node )
433 {
434 int bal = _node_balance(node);
435 if( bal == 2 ) {
436 node_link link = _get_parent_link(node);
437 if( _node_balance(node->left) == -1 ) {
438 _rotate_counterclockwise(
439 _make_link(node->left) );
440 _update_height( node->left->left );
441 _update_height( node->left );
442 }
443 _rotate_clockwise( link );
444 _update_height( node );
445 _update_height( _link_dest(link) );
446 return true;
447 }
448 else if( bal == -2 ) {
449 node_link link = _get_parent_link(node);
450 if( _node_balance(node->right) == 1 ) {
451 _rotate_clockwise( _make_link(node->right) );
452 _update_height( node->right->right );
453 _update_height( node->right );
454 }
455 _rotate_counterclockwise( link );
456 _update_height( node );
457 _update_height( _link_dest(link) );
458 return true;
459 }
460 return false;
461 }
462
463 // Balance the tree
464 void avl_tree::_balance( node_ptr node )
465 {
466 while( node ) {
467 if( _balance_node(node) )
468 node = node->parent->parent;
469 else if( ! _update_height(node) )
470 break;
471 else
472 node = node->parent;
473 }
474 }
475
476 // Check the tree for validity
477 int avl_tree::_check( node_ptr root )
478 {
479
480 if( root == nullptr )
481 return 0;
482
483 node_ptr left = root->left;
484 node_ptr right = root->right;
485
486 // Don't trust nodes' height values
487 int lh = _check(left);
488 int rh = _check(right);
489
490 // Make sure balance factor is not +2 or -2
491 if( lh - rh >= +2 )
492 throw "Right heavy!";
493 else if( lh - rh <= -2 )
494 throw "Left heavy!";
495
496 // Make sure left < right
497 if( left ) {
498 if( left->data > root->data )
499 throw "Not bst!";
500 }
501 if( right ) {
502 if( right->data < root->data )
503 throw "Not bst!";
504 }
505
506 // Make sure child->parent points to here
507 if( left )
508 if( left->parent != root )
509 throw "plink wrong!";
510 if( right )
511 if( right->parent != root )
512 throw "plink wrong!";
513
514 // Calculate height
515 int height = lh;
516 if( height < rh ) height = rh;
517 ++ height;
518
519 // Return results
520 return height;
521 }
522
523
524 // Insert data into the tree
525 void avl_tree::insert( const_reference value )
526 {
527 pair<link_type, node_ptr> where;
528 where = _get_insert_link( value );
529 if( ! where.first )
530 return;
531
532 node_ptr node = new tree_node;
533 node->data = value;
534 node->height = 1;
535 node->left = node->right = nullptr;
536 node->parent = where.second;
537 _link_set_dest( where.first, node );
538
539 _balance( node->parent );
540 }
541
542 // Erase data from the tree
543 void avl_tree::erase( const_reference value )
544 {
545 node_ptr node = find( value );
546 if( ! node )
547 return;
548 node = _replace_and_remove_node( node );
549 _balance( node->parent );
550 delete node;
551 }
552
553 // Make sure that the tree is valid
554 void avl_tree::check()
555 {
556 _check( _root );
557 }
558
559
560
561 /*
562 * Red-black tree
563 */
564
565 //
566 // Red-black tree class
567 //
568 class rb_tree : public binary_search_tree_base
569 {
570 protected:
571 // Helper functions
572 void _insert_harder_balancing( node_ptr );
573 void _insert_balance( node_ptr );
574 void _erase_harder_balancing_red_parent( node_ptr );
575 void _erase_harder_balancing( node_ptr );
576 void _erase_balance( node_ptr );
577 int _check( node_ptr );
578 public:
579 // Interface functions
580 void insert( const_reference );
581 void erase( const_reference );
582 void check();
583 };
584
585
586 // Balances tree after insertion; handle harder cases
587 void
588 rb_tree::_insert_harder_balancing(
589 node_ptr node )
590 {
591 node_ptr parent = node->parent;
592 node_ptr sibling = _node_sibling( node );
593
594 if( _node_color(sibling) == RED ) {
595 sibling->color = BLACK;
596 parent->color = RED;
597 if( _node_color(parent->parent) == RED ) {
598 parent->parent->color = BLACK;
599 _insert_harder_balancing( parent->parent );
600 }
601 }
602
603 else {
604 parent->color = RED;
605 if( node == parent->left ) {
606 if( _node_color(node->right) == RED ) {
607 node->color = RED;
608 node->right->color = BLACK;
609 _rotate_counterclockwise(
610 _make_link(parent->left) );
611 }
612 _rotate_clockwise(
613 _get_parent_link(parent) );
614 }
615 else {
616 if( _node_color(node->left) == RED ) {
617 node->color = RED;
618 node->left->color = BLACK;
619 _rotate_clockwise(
620 _make_link(parent->right) );
621 }
622 _rotate_counterclockwise(
623 _get_parent_link(parent) );
624 }
625 }
626 }
627
628 // Balance the tree after insertion; easy cases
629 void
630 rb_tree::_insert_balance(
631 node_ptr node )
632 {
633 if( ! node->parent ) {
634 node->color = BLACK;
635 }
636 else if( node->parent->color != BLACK ) {
637 node->parent->color = BLACK;
638 _insert_harder_balancing( node->parent );
639 _root->color = BLACK;
640 }
641 }
642
643 // Balance after erasing; harder; red parent
644 void
645 rb_tree::_erase_harder_balancing_red_parent(
646 node_ptr sibling )
647 {
648 node_ptr parent = sibling->parent;
649 if( sibling == parent->right ) {
650 if( _node_color(sibling->left) == RED ) {
651 parent->color = BLACK;
652 _rotate_clockwise( _make_link(parent->right) );
653 }
654 _rotate_counterclockwise( _get_parent_link(parent) );
655 }
656 else {
657 if( _node_color(sibling->right) == RED ) {
658 parent->color = BLACK;
659 _rotate_counterclockwise(
660 _make_link(parent->left) );
661 }
662 _rotate_clockwise( _get_parent_link(parent) );
663 }
664 }
665
666 // Balance after erasing; harder
667 void
668 rb_tree::_erase_harder_balancing(
669 node_ptr sibling )
670 {
671 // Use default rotations and sib->left|right
672 typedef void (rb_tree::*rot_func)( link_type );
673 rot_func rot_clock = & rb_tree::_rotate_clockwise;
674 rot_func rot_counter=& rb_tree::_rotate_counterclockwise;
675 node_ptr sib_left = sibling->left;
676 node_ptr sib_right = sibling->right;
677
678 // One more variable
679 node_ptr parent = sibling->parent;
680
681 // But if sibling is on the left, turn them around.
682 if( sibling == parent->left ) {
683 swap( rot_clock, rot_counter );
684 swap( sib_left, sib_right );
685 }
686
687 if( _node_color(parent) == BLACK ) {
688 if( sibling->color == BLACK ) {
689 if( _node_color(sib_left) == BLACK
690 && _node_color(sib_right) == BLACK ) {
691 sibling->color = RED;
692 if( parent->parent ) {
693 _erase_harder_balancing(
694 _node_sibling(parent) );
695 }
696 }
697 else {
698 if( _node_color(sib_left) == RED ) {
699 sib_left->color = BLACK;
700 (this->*rot_clock)(
701 _get_parent_link(sibling) );
702 }
703 else
704 sib_right->color = BLACK;
705 (this->*rot_counter)(
706 _get_parent_link(parent) );
707 }
708 }
709 else {
710 parent->color = RED;
711 sibling->color = BLACK;
712 (this->*rot_counter)( _get_parent_link(parent) );
713 _erase_harder_balancing_red_parent( sib_left );
714 }
715 }
716 else {
717 _erase_harder_balancing_red_parent( sibling );
718 }
719 }
720
721 // Balance the tree after erasing
722 void
723 rb_tree::_erase_balance(
724 node_ptr node )
725 {
726 if( _node_color(node) == BLACK ) {
727 if( node->left )
728 node->left->color = BLACK;
729 else if( node->right )
730 node->right->color = BLACK;
731 else if( node->parent ) {
732 node_ptr sib = node->parent->left;
733 if( ! sib )
734 sib = node->parent->right;
735 _erase_harder_balancing( sib );
736 }
737 }
738 }
739
740 // Check for correctness
741 int
742 rb_tree::_check(
743 node_ptr subtree )
744 {
745 // Imaginary leaf? black height is 1
746 if( ! subtree )
747 return 1;
748
749 node_ptr left = subtree->left;
750 node_ptr right = subtree->right;
751
752 // Black height of both sides must be the same
753 int left_height = _check( left );
754 int right_height = _check( right );
755 if( left_height != right_height )
756 throw "black imbalance!";
757
758 // No two reds in a row
759 if( _node_color(subtree) == RED ) {
760 if( _node_color(left) == RED
761 || _node_color(right) == RED )
762 throw "two reds in a row!";
763 }
764
765 // We're black, the height is [left|right]_height + 1
766 else
767 ++ left_height;
768
769 // Make sure that the children's parents are correct
770 if( (left && left->parent != subtree)
771 || (right && right->parent != subtree) )
772 throw "parent pointer wrong!";
773
774 // Return our height
775 return left_height;
776 }
777
778
779 // Insert data into the tree
780 void rb_tree::insert( const_reference value )
781 {
782 pair<link_type, node_ptr> where;
783 where = _get_insert_link( value );
784 if( ! where.first )
785 return;
786
787 node_ptr node = new tree_node;
788 node->data = value;
789 node->color = RED;
790 node->left = node->right = nullptr;
791 node->parent = where.second;
792 _link_set_dest( where.first, node );
793
794 _insert_balance( node );
795 }
796
797 // Erase data from the tree
798 void rb_tree::erase( const_reference value )
799 {
800 node_ptr node = find( value );
801 if( ! node )
802 return;
803 node = _replace_and_remove_node( node );
804 _erase_balance( node );
805 delete node;
806 }
807
808 // Make sure that the tree is valid
809 void rb_tree::check()
810 {
811 if( _node_color(_root) == RED )
812 throw "root is red!";
813 _check( _root );
814 }
815
816
817
818 /*
819 * Random number generation
820 */
821
822
823 //
824 // Random number generator class
825 // It always returns the exact same sequence after being
826 // reset. Also, it never returns a number twice.
827 // Max: SQRT_RAND_MAX
828 // Max Value: SQRT_RAND_MAX*SQRT_RAND_MAX - 1
829 //
830 class random_generator
831 {
832 int * _numbers;
833 int _major_index;
834 int _minor_index;
835 public:
836 // Interface
837 random_generator();
838 ~random_generator();
839 int get();
840 void reset();
841 }
842 random_number;
843
844
845 // Construct a random number generator
846 random_generator::random_generator() :
847 _numbers( new int[SQRT_RAND_MAX] ),
848 _major_index( 0 ),
849 _minor_index( 0 )
850 {
851 for( int i = 0; i != SQRT_RAND_MAX; ++i )
852 _numbers[i] = rand() % SQRT_RAND_MAX;
853 }
854
855 // Destructor
856 random_generator::~random_generator()
857 {
858 delete[] _numbers;
859 }
860
861 // Get a random number
862 int random_generator::get()
863 {
864 int ret = _numbers[_major_index] * SQRT_RAND_MAX
865 + _numbers[_minor_index];
866 ++ _major_index;
867 if( _major_index == SQRT_RAND_MAX ) {
868 _major_index = 0;
869 ++ _minor_index;
870 }
871 return ret;
872 }
873
874 // Reset the generator
875 void random_generator::reset()
876 {
877 _major_index = 0;
878 _minor_index = 0;
879 }
880
881
882
883 /*
884 * Timer
885 */
886
887
888 //
889 // Timer class
890 //
891 class code_timer
892 {
893 // Data
894 chrono::high_resolution_clock::time_point _t;
895 public:
896 void start();
897 void stop();
898 };
899
900
901 // Start the timer
902 void code_timer::start()
903 {
904 _t = chrono::high_resolution_clock::now();
905 }
906
907 // Stop the timer
908 void code_timer::stop()
909 {
910 chrono::microseconds ns
911 = chrono::high_resolution_clock::now() - _t;
912 cout << ns.count() << " microseconds" << endl;
913 }
914
915
916
917 /*
918 * Testing
919 */
920
921 //
922 // Test a tree
923 //
924 template<typename Tree>
925 void test( int count )
926 {
927 Tree tree;
928 code_timer timer;
929 int i;
930
931 cout << "--- Inserting in order ---" << endl;
932 timer.start();
933 for( i = 0; i != count; ++i ) {
934 tree.insert( i );
935 // tree.check();
936 }
937 timer.stop();
938
939 cout << "--- Searching all afterwards ---" << endl;
940 timer.start();
941 for( i = 0; i != count; ++i ) {
942 tree.find( i );
943 }
944 timer.stop();
945
946 cout << "--- Erasing in order ---" << endl;
947 timer.start();
948 for( i = 0; i != count; ++i ) {
949 tree.erase( i );
950 // tree.check();
951 }
952 timer.stop();
953
954 cout << "--- Inserting in random ---" << endl;
955 random_number.reset();
956 timer.start();
957 for( i = 0; i != count; ++i ) {
958 tree.insert( random_number.get() );
959 // tree.check();
960 }
961 timer.stop();
962
963 cout << "--- Searching all afterwards ---" << endl;
964 random_number.reset();
965 timer.start();
966 for( i = 0; i != count; ++i ) {
967 tree.find( random_number.get() );
968 }
969 timer.stop();
970
971 cout << "--- Erasing in random ---" << endl;
972 random_number.reset();
973 timer.start();
974 for( i = 0; i != count; ++i ) {
975 tree.erase( random_number.get() );
976 // tree.check();
977 }
978 timer.stop();
979 }
980
981
982
983 /*
984 * Main function
985 */
986
987 int main()
988 {
989 try {
990 code_timer timer;
991 timer.start();
992 for( int i = INTERVAL; i < INTERVAL*10; i += INTERVAL ) {
993 cout << endl << "AVL TREE, " << i << endl;
994 test<avl_tree>( i );
995 cout << endl << "RED-BLACK TREE, " << i << endl;
996 test<rb_tree>( i );
997 }
998 cout << endl << "TOTAL PROGRAM RUNNING TIME:" << endl;
999 timer.stop();
1000 }
1001 catch( const char * e ) {
1002 cout << e << endl;
1003 }
1004 };
1005
2 // AVL versus Red-Black
3 // written by Nathan Belue
4 // April 30, 2012 - May 1, 2012
5 //
6 // This is a comparison of AVL trees to red-black trees. It
7 // tests various scenarios and the speed of each tree in each
8 // scenario.
9 //
10
11
12 /*
13 * Included files
14 */
15
16 #include <chrono>
17 #include <cstdlib>
18 #include <iostream>
19
20 using namespace std;
21
22
23
24 /*
25 * Constants
26 */
27
28
29 namespace {
30
31 //
32 // Set this to how big of intervals to step
33 //
34 const int INTERVAL = 100000;
35
36
37 //
38 // The square root of the maximum random value
39 //
40 const int SQRT_RAND_MAX = 100000;
41
42 }
43
44
45
46 /*
47 * Binary search tree base
48 */
49
50
51 //
52 // Value being stored
53 //
54 typedef int value_type;
55 typedef const value_type & const_reference;
56
57
58 //
59 // More typedefs
60 //
61 typedef size_t size_type;
62
63
64 //
65 // Colors
66 //
67 enum color_type
68 {
69 RED,
70 BLACK
71 };
72
73
74 //
75 // A node of a tree
76 //
77 struct tree_node
78 {
79 value_type data;
80 tree_node * parent;
81 tree_node * left;
82 tree_node * right;
83 union {
84 color_type color;
85 size_type height;
86 };
87 };
88 typedef tree_node * node_ptr;
89 typedef node_ptr * link_type;
90
91
92 //
93 // Binary search tree base class
94 //
95 class binary_search_tree_base
96 {
97 protected:
98 // Data
99 node_ptr _root;
100
101 private:
102 // bst helper functions
103 void _clear( node_ptr );
104
105 protected:
106 // Links
107 typedef node_ptr * node_link;
108 link_type _make_link( node_ptr & );
109 link_type _get_parent_link( node_ptr );
110 node_ptr _link_dest( node_link );
111 void _link_set_dest( node_link, node_ptr );
112 node_ptr _link_origin( node_link );
113
114 protected:
115 // Node functions
116 color_type _node_color( node_ptr );
117 size_type _node_height( node_ptr );
118 node_ptr _node_sibling( node_ptr );
119 node_ptr _node_leftmost( node_ptr );
120
121 protected:
122 // Helper functions
123 void _rotate_clockwise( link_type node );
124 void _rotate_counterclockwise( link_type node );
125 pair<link_type, node_ptr> _get_insert_link(
126 const_reference );
127 node_ptr _replace_and_remove_node( node_ptr );
128
129 public:
130 // Interface
131 binary_search_tree_base();
132 ~binary_search_tree_base();
133 void clear();
134 node_ptr find( const_reference );
135 bool is_empty();
136 };
137
138
139 // Recursively clear the tree
140 void
141 binary_search_tree_base::_clear(
142 node_ptr node )
143 {
144 if( node ) {
145 _clear( node->left );
146 _clear( node->right );
147 delete node;
148 }
149 }
150
151
152 // Convert a node pointer to a link
153 link_type
154 binary_search_tree_base::_make_link(
155 node_ptr & node )
156 {
157 return & node;
158 }
159
160 // Get a node's parent's link to that node
161 link_type
162 binary_search_tree_base::_get_parent_link(
163 node_ptr node )
164 {
165 node_ptr parent = node->parent;
166 if( ! parent )
167 return _make_link( _root );
168 if( parent->left == node )
169 return _make_link( parent->left );
170 return _make_link( parent->right );
171 }
172
173 // Get a link's destination
174 node_ptr
175 binary_search_tree_base::_link_dest(
176 link_type link )
177 {
178 return *link;
179 }
180
181 // Set a link's destination
182 void
183 binary_search_tree_base::_link_set_dest(
184 link_type link, node_ptr dest )
185 {
186 *link = dest;
187 }
188
189 // Get a link's origin
190 node_ptr
191 binary_search_tree_base::_link_origin(
192 node_link link )
193 {
194 return _link_dest(link) -> parent;
195 }
196
197
198 // Get a node's color
199 // Also works for nonexistent imaginary black leaf nodes
200 color_type
201 binary_search_tree_base::_node_color(
202 node_ptr node )
203 {
204 if( node )
205 return node->color;
206 return BLACK;
207 }
208
209 // Get a node's height
210 size_type
211 binary_search_tree_base::_node_height(
212 node_ptr node )
213 {
214 if( node )
215 return node->height;
216 return 0;
217 }
218
219 // Get a node's sibling
220 node_ptr
221 binary_search_tree_base::_node_sibling(
222 node_ptr node )
223 {
224 node_ptr par = node->parent;
225 node_ptr sib = par->left;
226 if( sib == node )
227 sib = par->right;
228 return sib;
229 }
230
231 // Return the leftmost node of a subtree
232 node_ptr
233 binary_search_tree_base::_node_leftmost(
234 node_ptr node )
235 {
236 while( node->left )
237 node = node->left;
238 return node;
239 }
240
241
242 // Rotate about a node clockwise
243 void
244 binary_search_tree_base::_rotate_clockwise(
245 link_type link )
246 {
247 node_ptr node = _link_dest(link);
248 node_ptr left = node->left;
249 node_ptr lright = left->right;
250
251 _link_set_dest( link, left );
252 left->parent = node->parent;
253
254 left->right = node;
255 node->parent = left;
256
257 node->left = lright;
258 if( lright )
259 lright->parent = node;
260 }
261
262 // Rotate about a node counterclockwise
263 void
264 binary_search_tree_base::_rotate_counterclockwise(
265 link_type link )
266 {
267 node_ptr node = _link_dest(link);
268 node_ptr right = node->right;
269 node_ptr rleft = right->left;
270
271 _link_set_dest( link, right );
272 right->parent = node->parent;
273
274 right->left = node;
275 node->parent = right;
276
277 node->right = rleft;
278 if( rleft )
279 rleft->parent = node;
280 }
281
282 // Figure out where to insert a value into the tree
283 // Returns the link and its origin
284 pair<link_type, node_ptr>
285 binary_search_tree_base::_get_insert_link(
286 const_reference value )
287 {
288 link_type where = _make_link(_root);
289 node_ptr origin = nullptr;
290
291 while( _link_dest(where) ) {
292 origin = _link_dest(where);
293 if( value < origin->data )
294 where = _make_link( origin->left );
295 else if( value > origin->data )
296 where = _make_link( origin->right );
297 else {
298 where = nullptr;
299 break;
300 }
301 }
302
303 return make_pair( where, origin );
304 }
305
306 // Replace and remove node
307 // Returns actual node removed
308 node_ptr
309 binary_search_tree_base::_replace_and_remove_node(
310 node_ptr node )
311 {
312 node_ptr rep = nullptr;
313
314 if( node->left && node->right ) {
315 rep = _node_leftmost( node->right );
316 std::swap( rep->data, node->data );
317 node = rep;
318 rep = nullptr;
319 }
320
321 if( node->left )
322 rep = node->left;
323 else if( node->right )
324 rep = node->right;
325
326 _link_set_dest( _get_parent_link(node), rep );
327 if( rep )
328 rep->parent = node->parent;
329
330 return node;
331 }
332
333
334 // Constructor
335 binary_search_tree_base::binary_search_tree_base() :
336 _root( nullptr )
337 {
338 }
339
340 // Destructor
341 binary_search_tree_base::~binary_search_tree_base()
342 {
343 _clear( _root );
344 }
345
346 // Clear the tree
347 void
348 binary_search_tree_base::clear()
349 {
350 _clear( _root );
351 _root = nullptr;
352 }
353
354 // Find a value in the tree
355 node_ptr
356 binary_search_tree_base::find(
357 const_reference value )
358 {
359 node_ptr node = _root;
360 while( node ) {
361 if( value < node->data )
362 node = node->left;
363 else if( value > node->data )
364 node = node->right;
365 else
366 break;
367 }
368 return node;
369 }
370
371 // Is the tree empty?
372 bool
373 binary_search_tree_base::is_empty()
374 {
375 return _root == nullptr;
376 }
377
378
379
380 /*
381 * AVL tree
382 */
383
384
385 //
386 // AVL tree class
387 //
388 class avl_tree : public binary_search_tree_base
389 {
390 protected:
391 // Node functions
392 int _node_balance( node_ptr );
393 protected:
394 // Helper functions
395 bool _update_height( node_ptr );
396 bool _balance_node( node_ptr );
397 void _balance( node_ptr );
398 int _check( node_ptr );
399 public:
400 // Interface functions
401 void insert( const_reference );
402 void erase( const_reference );
403 void check();
404 };
405
406
407 // Balance of a node
408 int avl_tree::_node_balance( node_ptr node )
409 {
410 return _node_height(node->left)
411 - _node_height(node->right);
412 }
413
414
415 // Update the height of anode
416 // Returns true if height was changed
417 bool avl_tree::_update_height( node_ptr node )
418 {
419 if( ! node )
420 return false;
421 size_type oldH = node->height;
422 size_type leftH = _node_height( node->left );
423 size_type rightH = _node_height( node->right );
424 if( leftH > rightH )
425 node->height = leftH + 1;
426 else
427 node->height = rightH + 1;
428 return ! (oldH == node->height);
429 }
430
431 // Balance at node; return true if balance was done
432 bool avl_tree::_balance_node( node_ptr node )
433 {
434 int bal = _node_balance(node);
435 if( bal == 2 ) {
436 node_link link = _get_parent_link(node);
437 if( _node_balance(node->left) == -1 ) {
438 _rotate_counterclockwise(
439 _make_link(node->left) );
440 _update_height( node->left->left );
441 _update_height( node->left );
442 }
443 _rotate_clockwise( link );
444 _update_height( node );
445 _update_height( _link_dest(link) );
446 return true;
447 }
448 else if( bal == -2 ) {
449 node_link link = _get_parent_link(node);
450 if( _node_balance(node->right) == 1 ) {
451 _rotate_clockwise( _make_link(node->right) );
452 _update_height( node->right->right );
453 _update_height( node->right );
454 }
455 _rotate_counterclockwise( link );
456 _update_height( node );
457 _update_height( _link_dest(link) );
458 return true;
459 }
460 return false;
461 }
462
463 // Balance the tree
464 void avl_tree::_balance( node_ptr node )
465 {
466 while( node ) {
467 if( _balance_node(node) )
468 node = node->parent->parent;
469 else if( ! _update_height(node) )
470 break;
471 else
472 node = node->parent;
473 }
474 }
475
476 // Check the tree for validity
477 int avl_tree::_check( node_ptr root )
478 {
479
480 if( root == nullptr )
481 return 0;
482
483 node_ptr left = root->left;
484 node_ptr right = root->right;
485
486 // Don't trust nodes' height values
487 int lh = _check(left);
488 int rh = _check(right);
489
490 // Make sure balance factor is not +2 or -2
491 if( lh - rh >= +2 )
492 throw "Right heavy!";
493 else if( lh - rh <= -2 )
494 throw "Left heavy!";
495
496 // Make sure left < right
497 if( left ) {
498 if( left->data > root->data )
499 throw "Not bst!";
500 }
501 if( right ) {
502 if( right->data < root->data )
503 throw "Not bst!";
504 }
505
506 // Make sure child->parent points to here
507 if( left )
508 if( left->parent != root )
509 throw "plink wrong!";
510 if( right )
511 if( right->parent != root )
512 throw "plink wrong!";
513
514 // Calculate height
515 int height = lh;
516 if( height < rh ) height = rh;
517 ++ height;
518
519 // Return results
520 return height;
521 }
522
523
524 // Insert data into the tree
525 void avl_tree::insert( const_reference value )
526 {
527 pair<link_type, node_ptr> where;
528 where = _get_insert_link( value );
529 if( ! where.first )
530 return;
531
532 node_ptr node = new tree_node;
533 node->data = value;
534 node->height = 1;
535 node->left = node->right = nullptr;
536 node->parent = where.second;
537 _link_set_dest( where.first, node );
538
539 _balance( node->parent );
540 }
541
542 // Erase data from the tree
543 void avl_tree::erase( const_reference value )
544 {
545 node_ptr node = find( value );
546 if( ! node )
547 return;
548 node = _replace_and_remove_node( node );
549 _balance( node->parent );
550 delete node;
551 }
552
553 // Make sure that the tree is valid
554 void avl_tree::check()
555 {
556 _check( _root );
557 }
558
559
560
561 /*
562 * Red-black tree
563 */
564
565 //
566 // Red-black tree class
567 //
568 class rb_tree : public binary_search_tree_base
569 {
570 protected:
571 // Helper functions
572 void _insert_harder_balancing( node_ptr );
573 void _insert_balance( node_ptr );
574 void _erase_harder_balancing_red_parent( node_ptr );
575 void _erase_harder_balancing( node_ptr );
576 void _erase_balance( node_ptr );
577 int _check( node_ptr );
578 public:
579 // Interface functions
580 void insert( const_reference );
581 void erase( const_reference );
582 void check();
583 };
584
585
586 // Balances tree after insertion; handle harder cases
587 void
588 rb_tree::_insert_harder_balancing(
589 node_ptr node )
590 {
591 node_ptr parent = node->parent;
592 node_ptr sibling = _node_sibling( node );
593
594 if( _node_color(sibling) == RED ) {
595 sibling->color = BLACK;
596 parent->color = RED;
597 if( _node_color(parent->parent) == RED ) {
598 parent->parent->color = BLACK;
599 _insert_harder_balancing( parent->parent );
600 }
601 }
602
603 else {
604 parent->color = RED;
605 if( node == parent->left ) {
606 if( _node_color(node->right) == RED ) {
607 node->color = RED;
608 node->right->color = BLACK;
609 _rotate_counterclockwise(
610 _make_link(parent->left) );
611 }
612 _rotate_clockwise(
613 _get_parent_link(parent) );
614 }
615 else {
616 if( _node_color(node->left) == RED ) {
617 node->color = RED;
618 node->left->color = BLACK;
619 _rotate_clockwise(
620 _make_link(parent->right) );
621 }
622 _rotate_counterclockwise(
623 _get_parent_link(parent) );
624 }
625 }
626 }
627
628 // Balance the tree after insertion; easy cases
629 void
630 rb_tree::_insert_balance(
631 node_ptr node )
632 {
633 if( ! node->parent ) {
634 node->color = BLACK;
635 }
636 else if( node->parent->color != BLACK ) {
637 node->parent->color = BLACK;
638 _insert_harder_balancing( node->parent );
639 _root->color = BLACK;
640 }
641 }
642
643 // Balance after erasing; harder; red parent
644 void
645 rb_tree::_erase_harder_balancing_red_parent(
646 node_ptr sibling )
647 {
648 node_ptr parent = sibling->parent;
649 if( sibling == parent->right ) {
650 if( _node_color(sibling->left) == RED ) {
651 parent->color = BLACK;
652 _rotate_clockwise( _make_link(parent->right) );
653 }
654 _rotate_counterclockwise( _get_parent_link(parent) );
655 }
656 else {
657 if( _node_color(sibling->right) == RED ) {
658 parent->color = BLACK;
659 _rotate_counterclockwise(
660 _make_link(parent->left) );
661 }
662 _rotate_clockwise( _get_parent_link(parent) );
663 }
664 }
665
666 // Balance after erasing; harder
667 void
668 rb_tree::_erase_harder_balancing(
669 node_ptr sibling )
670 {
671 // Use default rotations and sib->left|right
672 typedef void (rb_tree::*rot_func)( link_type );
673 rot_func rot_clock = & rb_tree::_rotate_clockwise;
674 rot_func rot_counter=& rb_tree::_rotate_counterclockwise;
675 node_ptr sib_left = sibling->left;
676 node_ptr sib_right = sibling->right;
677
678 // One more variable
679 node_ptr parent = sibling->parent;
680
681 // But if sibling is on the left, turn them around.
682 if( sibling == parent->left ) {
683 swap( rot_clock, rot_counter );
684 swap( sib_left, sib_right );
685 }
686
687 if( _node_color(parent) == BLACK ) {
688 if( sibling->color == BLACK ) {
689 if( _node_color(sib_left) == BLACK
690 && _node_color(sib_right) == BLACK ) {
691 sibling->color = RED;
692 if( parent->parent ) {
693 _erase_harder_balancing(
694 _node_sibling(parent) );
695 }
696 }
697 else {
698 if( _node_color(sib_left) == RED ) {
699 sib_left->color = BLACK;
700 (this->*rot_clock)(
701 _get_parent_link(sibling) );
702 }
703 else
704 sib_right->color = BLACK;
705 (this->*rot_counter)(
706 _get_parent_link(parent) );
707 }
708 }
709 else {
710 parent->color = RED;
711 sibling->color = BLACK;
712 (this->*rot_counter)( _get_parent_link(parent) );
713 _erase_harder_balancing_red_parent( sib_left );
714 }
715 }
716 else {
717 _erase_harder_balancing_red_parent( sibling );
718 }
719 }
720
721 // Balance the tree after erasing
722 void
723 rb_tree::_erase_balance(
724 node_ptr node )
725 {
726 if( _node_color(node) == BLACK ) {
727 if( node->left )
728 node->left->color = BLACK;
729 else if( node->right )
730 node->right->color = BLACK;
731 else if( node->parent ) {
732 node_ptr sib = node->parent->left;
733 if( ! sib )
734 sib = node->parent->right;
735 _erase_harder_balancing( sib );
736 }
737 }
738 }
739
740 // Check for correctness
741 int
742 rb_tree::_check(
743 node_ptr subtree )
744 {
745 // Imaginary leaf? black height is 1
746 if( ! subtree )
747 return 1;
748
749 node_ptr left = subtree->left;
750 node_ptr right = subtree->right;
751
752 // Black height of both sides must be the same
753 int left_height = _check( left );
754 int right_height = _check( right );
755 if( left_height != right_height )
756 throw "black imbalance!";
757
758 // No two reds in a row
759 if( _node_color(subtree) == RED ) {
760 if( _node_color(left) == RED
761 || _node_color(right) == RED )
762 throw "two reds in a row!";
763 }
764
765 // We're black, the height is [left|right]_height + 1
766 else
767 ++ left_height;
768
769 // Make sure that the children's parents are correct
770 if( (left && left->parent != subtree)
771 || (right && right->parent != subtree) )
772 throw "parent pointer wrong!";
773
774 // Return our height
775 return left_height;
776 }
777
778
779 // Insert data into the tree
780 void rb_tree::insert( const_reference value )
781 {
782 pair<link_type, node_ptr> where;
783 where = _get_insert_link( value );
784 if( ! where.first )
785 return;
786
787 node_ptr node = new tree_node;
788 node->data = value;
789 node->color = RED;
790 node->left = node->right = nullptr;
791 node->parent = where.second;
792 _link_set_dest( where.first, node );
793
794 _insert_balance( node );
795 }
796
797 // Erase data from the tree
798 void rb_tree::erase( const_reference value )
799 {
800 node_ptr node = find( value );
801 if( ! node )
802 return;
803 node = _replace_and_remove_node( node );
804 _erase_balance( node );
805 delete node;
806 }
807
808 // Make sure that the tree is valid
809 void rb_tree::check()
810 {
811 if( _node_color(_root) == RED )
812 throw "root is red!";
813 _check( _root );
814 }
815
816
817
818 /*
819 * Random number generation
820 */
821
822
823 //
824 // Random number generator class
825 // It always returns the exact same sequence after being
826 // reset. Also, it never returns a number twice.
827 // Max: SQRT_RAND_MAX
828 // Max Value: SQRT_RAND_MAX*SQRT_RAND_MAX - 1
829 //
830 class random_generator
831 {
832 int * _numbers;
833 int _major_index;
834 int _minor_index;
835 public:
836 // Interface
837 random_generator();
838 ~random_generator();
839 int get();
840 void reset();
841 }
842 random_number;
843
844
845 // Construct a random number generator
846 random_generator::random_generator() :
847 _numbers( new int[SQRT_RAND_MAX] ),
848 _major_index( 0 ),
849 _minor_index( 0 )
850 {
851 for( int i = 0; i != SQRT_RAND_MAX; ++i )
852 _numbers[i] = rand() % SQRT_RAND_MAX;
853 }
854
855 // Destructor
856 random_generator::~random_generator()
857 {
858 delete[] _numbers;
859 }
860
861 // Get a random number
862 int random_generator::get()
863 {
864 int ret = _numbers[_major_index] * SQRT_RAND_MAX
865 + _numbers[_minor_index];
866 ++ _major_index;
867 if( _major_index == SQRT_RAND_MAX ) {
868 _major_index = 0;
869 ++ _minor_index;
870 }
871 return ret;
872 }
873
874 // Reset the generator
875 void random_generator::reset()
876 {
877 _major_index = 0;
878 _minor_index = 0;
879 }
880
881
882
883 /*
884 * Timer
885 */
886
887
888 //
889 // Timer class
890 //
891 class code_timer
892 {
893 // Data
894 chrono::high_resolution_clock::time_point _t;
895 public:
896 void start();
897 void stop();
898 };
899
900
901 // Start the timer
902 void code_timer::start()
903 {
904 _t = chrono::high_resolution_clock::now();
905 }
906
907 // Stop the timer
908 void code_timer::stop()
909 {
910 chrono::microseconds ns
911 = chrono::high_resolution_clock::now() - _t;
912 cout << ns.count() << " microseconds" << endl;
913 }
914
915
916
917 /*
918 * Testing
919 */
920
921 //
922 // Test a tree
923 //
924 template<typename Tree>
925 void test( int count )
926 {
927 Tree tree;
928 code_timer timer;
929 int i;
930
931 cout << "--- Inserting in order ---" << endl;
932 timer.start();
933 for( i = 0; i != count; ++i ) {
934 tree.insert( i );
935 // tree.check();
936 }
937 timer.stop();
938
939 cout << "--- Searching all afterwards ---" << endl;
940 timer.start();
941 for( i = 0; i != count; ++i ) {
942 tree.find( i );
943 }
944 timer.stop();
945
946 cout << "--- Erasing in order ---" << endl;
947 timer.start();
948 for( i = 0; i != count; ++i ) {
949 tree.erase( i );
950 // tree.check();
951 }
952 timer.stop();
953
954 cout << "--- Inserting in random ---" << endl;
955 random_number.reset();
956 timer.start();
957 for( i = 0; i != count; ++i ) {
958 tree.insert( random_number.get() );
959 // tree.check();
960 }
961 timer.stop();
962
963 cout << "--- Searching all afterwards ---" << endl;
964 random_number.reset();
965 timer.start();
966 for( i = 0; i != count; ++i ) {
967 tree.find( random_number.get() );
968 }
969 timer.stop();
970
971 cout << "--- Erasing in random ---" << endl;
972 random_number.reset();
973 timer.start();
974 for( i = 0; i != count; ++i ) {
975 tree.erase( random_number.get() );
976 // tree.check();
977 }
978 timer.stop();
979 }
980
981
982
983 /*
984 * Main function
985 */
986
987 int main()
988 {
989 try {
990 code_timer timer;
991 timer.start();
992 for( int i = INTERVAL; i < INTERVAL*10; i += INTERVAL ) {
993 cout << endl << "AVL TREE, " << i << endl;
994 test<avl_tree>( i );
995 cout << endl << "RED-BLACK TREE, " << i << endl;
996 test<rb_tree>( i );
997 }
998 cout << endl << "TOTAL PROGRAM RUNNING TIME:" << endl;
999 timer.stop();
1000 }
1001 catch( const char * e ) {
1002 cout << e << endl;
1003 }
1004 };
1005
Results:
The output is as follows.
AVL TREE, 100000
--- Inserting in order ---
17114 microseconds
--- Searching all afterwards ---
6844 microseconds
--- Erasing in order ---
12577 microseconds
--- Inserting in random ---
46528 microseconds
--- Searching all afterwards ---
36651 microseconds
--- Erasing in random ---
36527 microseconds
RED-BLACK TREE, 100000
--- Inserting in order ---
17711 microseconds
--- Searching all afterwards ---
11772 microseconds
--- Erasing in order ---
14304 microseconds
--- Inserting in random ---
31337 microseconds
--- Searching all afterwards ---
27948 microseconds
--- Erasing in random ---
27852 microseconds
AVL TREE, 200000
--- Inserting in order ---
36405 microseconds
--- Searching all afterwards ---
17925 microseconds
--- Erasing in order ---
28306 microseconds
--- Inserting in random ---
70557 microseconds
--- Searching all afterwards ---
55740 microseconds
--- Erasing in random ---
70785 microseconds
RED-BLACK TREE, 200000
--- Inserting in order ---
37151 microseconds
--- Searching all afterwards ---
23832 microseconds
--- Erasing in order ---
30406 microseconds
--- Inserting in random ---
73643 microseconds
--- Searching all afterwards ---
80067 microseconds
--- Erasing in random ---
90427 microseconds
AVL TREE, 300000
--- Inserting in order ---
64436 microseconds
--- Searching all afterwards ---
37097 microseconds
--- Erasing in order ---
53168 microseconds
--- Inserting in random ---
153341 microseconds
--- Searching all afterwards ---
134494 microseconds
--- Erasing in random ---
156761 microseconds
RED-BLACK TREE, 300000
--- Inserting in order ---
69095 microseconds
--- Searching all afterwards ---
46417 microseconds
--- Erasing in order ---
53712 microseconds
--- Inserting in random ---
183088 microseconds
--- Searching all afterwards ---
145537 microseconds
--- Erasing in random ---
141416 microseconds
AVL TREE, 400000
--- Inserting in order ---
87766 microseconds
--- Searching all afterwards ---
50195 microseconds
--- Erasing in order ---
68301 microseconds
--- Inserting in random ---
226314 microseconds
--- Searching all afterwards ---
198646 microseconds
--- Erasing in random ---
241950 microseconds
RED-BLACK TREE, 400000
--- Inserting in order ---
96927 microseconds
--- Searching all afterwards ---
63858 microseconds
--- Erasing in order ---
79998 microseconds
--- Inserting in random ---
282133 microseconds
--- Searching all afterwards ---
206998 microseconds
--- Erasing in random ---
227705 microseconds
AVL TREE, 500000
--- Inserting in order ---
116793 microseconds
--- Searching all afterwards ---
72019 microseconds
--- Erasing in order ---
92173 microseconds
--- Inserting in random ---
343159 microseconds
--- Searching all afterwards ---
279647 microseconds
--- Erasing in random ---
356676 microseconds
RED-BLACK TREE, 500000
--- Inserting in order ---
122283 microseconds
--- Searching all afterwards ---
79625 microseconds
--- Erasing in order ---
93864 microseconds
--- Inserting in random ---
370758 microseconds
--- Searching all afterwards ---
381809 microseconds
--- Erasing in random ---
399800 microseconds
AVL TREE, 600000
--- Inserting in order ---
152210 microseconds
--- Searching all afterwards ---
90795 microseconds
--- Erasing in order ---
114050 microseconds
--- Inserting in random ---
389289 microseconds
--- Searching all afterwards ---
365971 microseconds
--- Erasing in random ---
482464 microseconds
RED-BLACK TREE, 600000
--- Inserting in order ---
155385 microseconds
--- Searching all afterwards ---
101590 microseconds
--- Erasing in order ---
116332 microseconds
--- Inserting in random ---
449050 microseconds
--- Searching all afterwards ---
483760 microseconds
--- Erasing in random ---
530612 microseconds
AVL TREE, 700000
--- Inserting in order ---
181521 microseconds
--- Searching all afterwards ---
108327 microseconds
--- Erasing in order ---
131930 microseconds
--- Inserting in random ---
531229 microseconds
--- Searching all afterwards ---
475930 microseconds
--- Erasing in random ---
524804 microseconds
RED-BLACK TREE, 700000
--- Inserting in order ---
189916 microseconds
--- Searching all afterwards ---
121527 microseconds
--- Erasing in order ---
147624 microseconds
--- Inserting in random ---
662187 microseconds
--- Searching all afterwards ---
545392 microseconds
--- Erasing in random ---
634577 microseconds
AVL TREE, 800000
--- Inserting in order ---
212977 microseconds
--- Searching all afterwards ---
133794 microseconds
--- Erasing in order ---
161690 microseconds
--- Inserting in random ---
617316 microseconds
--- Searching all afterwards ---
543345 microseconds
--- Erasing in random ---
663574 microseconds
RED-BLACK TREE, 800000
--- Inserting in order ---
209157 microseconds
--- Searching all afterwards ---
137076 microseconds
--- Erasing in order ---
164212 microseconds
--- Inserting in random ---
777456 microseconds
--- Searching all afterwards ---
662227 microseconds
--- Erasing in random ---
761854 microseconds
AVL TREE, 900000
--- Inserting in order ---
244121 microseconds
--- Searching all afterwards ---
151686 microseconds
--- Erasing in order ---
178633 microseconds
--- Inserting in random ---
750362 microseconds
--- Searching all afterwards ---
661424 microseconds
--- Erasing in random ---
722931 microseconds
RED-BLACK TREE, 900000
--- Inserting in order ---
256723 microseconds
--- Searching all afterwards ---
162953 microseconds
--- Erasing in order ---
190804 microseconds
--- Inserting in random ---
875096 microseconds
--- Searching all afterwards ---
825393 microseconds
--- Erasing in random ---
852197 microseconds
TOTAL PROGRAM RUNNING TIME:
25471323 microseconds
--- Inserting in order ---
17114 microseconds
--- Searching all afterwards ---
6844 microseconds
--- Erasing in order ---
12577 microseconds
--- Inserting in random ---
46528 microseconds
--- Searching all afterwards ---
36651 microseconds
--- Erasing in random ---
36527 microseconds
RED-BLACK TREE, 100000
--- Inserting in order ---
17711 microseconds
--- Searching all afterwards ---
11772 microseconds
--- Erasing in order ---
14304 microseconds
--- Inserting in random ---
31337 microseconds
--- Searching all afterwards ---
27948 microseconds
--- Erasing in random ---
27852 microseconds
AVL TREE, 200000
--- Inserting in order ---
36405 microseconds
--- Searching all afterwards ---
17925 microseconds
--- Erasing in order ---
28306 microseconds
--- Inserting in random ---
70557 microseconds
--- Searching all afterwards ---
55740 microseconds
--- Erasing in random ---
70785 microseconds
RED-BLACK TREE, 200000
--- Inserting in order ---
37151 microseconds
--- Searching all afterwards ---
23832 microseconds
--- Erasing in order ---
30406 microseconds
--- Inserting in random ---
73643 microseconds
--- Searching all afterwards ---
80067 microseconds
--- Erasing in random ---
90427 microseconds
AVL TREE, 300000
--- Inserting in order ---
64436 microseconds
--- Searching all afterwards ---
37097 microseconds
--- Erasing in order ---
53168 microseconds
--- Inserting in random ---
153341 microseconds
--- Searching all afterwards ---
134494 microseconds
--- Erasing in random ---
156761 microseconds
RED-BLACK TREE, 300000
--- Inserting in order ---
69095 microseconds
--- Searching all afterwards ---
46417 microseconds
--- Erasing in order ---
53712 microseconds
--- Inserting in random ---
183088 microseconds
--- Searching all afterwards ---
145537 microseconds
--- Erasing in random ---
141416 microseconds
AVL TREE, 400000
--- Inserting in order ---
87766 microseconds
--- Searching all afterwards ---
50195 microseconds
--- Erasing in order ---
68301 microseconds
--- Inserting in random ---
226314 microseconds
--- Searching all afterwards ---
198646 microseconds
--- Erasing in random ---
241950 microseconds
RED-BLACK TREE, 400000
--- Inserting in order ---
96927 microseconds
--- Searching all afterwards ---
63858 microseconds
--- Erasing in order ---
79998 microseconds
--- Inserting in random ---
282133 microseconds
--- Searching all afterwards ---
206998 microseconds
--- Erasing in random ---
227705 microseconds
AVL TREE, 500000
--- Inserting in order ---
116793 microseconds
--- Searching all afterwards ---
72019 microseconds
--- Erasing in order ---
92173 microseconds
--- Inserting in random ---
343159 microseconds
--- Searching all afterwards ---
279647 microseconds
--- Erasing in random ---
356676 microseconds
RED-BLACK TREE, 500000
--- Inserting in order ---
122283 microseconds
--- Searching all afterwards ---
79625 microseconds
--- Erasing in order ---
93864 microseconds
--- Inserting in random ---
370758 microseconds
--- Searching all afterwards ---
381809 microseconds
--- Erasing in random ---
399800 microseconds
AVL TREE, 600000
--- Inserting in order ---
152210 microseconds
--- Searching all afterwards ---
90795 microseconds
--- Erasing in order ---
114050 microseconds
--- Inserting in random ---
389289 microseconds
--- Searching all afterwards ---
365971 microseconds
--- Erasing in random ---
482464 microseconds
RED-BLACK TREE, 600000
--- Inserting in order ---
155385 microseconds
--- Searching all afterwards ---
101590 microseconds
--- Erasing in order ---
116332 microseconds
--- Inserting in random ---
449050 microseconds
--- Searching all afterwards ---
483760 microseconds
--- Erasing in random ---
530612 microseconds
AVL TREE, 700000
--- Inserting in order ---
181521 microseconds
--- Searching all afterwards ---
108327 microseconds
--- Erasing in order ---
131930 microseconds
--- Inserting in random ---
531229 microseconds
--- Searching all afterwards ---
475930 microseconds
--- Erasing in random ---
524804 microseconds
RED-BLACK TREE, 700000
--- Inserting in order ---
189916 microseconds
--- Searching all afterwards ---
121527 microseconds
--- Erasing in order ---
147624 microseconds
--- Inserting in random ---
662187 microseconds
--- Searching all afterwards ---
545392 microseconds
--- Erasing in random ---
634577 microseconds
AVL TREE, 800000
--- Inserting in order ---
212977 microseconds
--- Searching all afterwards ---
133794 microseconds
--- Erasing in order ---
161690 microseconds
--- Inserting in random ---
617316 microseconds
--- Searching all afterwards ---
543345 microseconds
--- Erasing in random ---
663574 microseconds
RED-BLACK TREE, 800000
--- Inserting in order ---
209157 microseconds
--- Searching all afterwards ---
137076 microseconds
--- Erasing in order ---
164212 microseconds
--- Inserting in random ---
777456 microseconds
--- Searching all afterwards ---
662227 microseconds
--- Erasing in random ---
761854 microseconds
AVL TREE, 900000
--- Inserting in order ---
244121 microseconds
--- Searching all afterwards ---
151686 microseconds
--- Erasing in order ---
178633 microseconds
--- Inserting in random ---
750362 microseconds
--- Searching all afterwards ---
661424 microseconds
--- Erasing in random ---
722931 microseconds
RED-BLACK TREE, 900000
--- Inserting in order ---
256723 microseconds
--- Searching all afterwards ---
162953 microseconds
--- Erasing in order ---
190804 microseconds
--- Inserting in random ---
875096 microseconds
--- Searching all afterwards ---
825393 microseconds
--- Erasing in random ---
852197 microseconds
TOTAL PROGRAM RUNNING TIME:
25471323 microseconds
The above is kind of messy, so I have put the data into a graph to make it easier to see what happens.
Analysis:
Okay, the results are not even close to what I expected! Why does the AVL tree beat the red-black tree when manipulating random data? And for ordered data, though it is not immediately apparent by the graph, analyzing the data shows the the AVL tree again wins.
Because the results obtained may be due to the fact that I am much better at coding AVL trees than at coding red-black trees, I ran another test to confirm the accuracy of the results. I have an AVL tree that I coded a few months ago (I do not provide the source), and I tested that against std::set using the same method. The AVL tree again won.
The results of the operations on ordered data are too close together for comfort, so that needs tested again with larger numbers.
Re-test ordered data:
In the code, I have commented out the tests on random data, and I have changed the INTERVAL and SQRT_MAX_RAND constants.
1 //
2 // AVL versus Red-Black
3 // written by Nathan Belue
4 // April 30, 2012 - May 1, 2012
5 //
6 // This is a comparison of AVL trees to red-black trees. It
7 // tests various scenarios and the speed of each tree in each
8 // scenario.
9 //
10
11
12 /*
13 * Included files
14 */
15
16 #include <chrono>
17 #include <cstdlib>
18 #include <iostream>
19
20 using namespace std;
21
22
23
24 /*
25 * Constants
26 */
27
28
29 namespace {
30
31 //
32 // Set this to how big of intervals to step
33 //
34 const int INTERVAL = 1000000;
35
36
37 //
38 // The square root of the maximum random value
39 //
40 const int SQRT_RAND_MAX = 1000000;
41
42 }
43
44
45
46 /*
47 * Binary search tree base
48 */
49
50
51 //
52 // Value being stored
53 //
54 typedef int value_type;
55 typedef const value_type & const_reference;
56
57
58 //
59 // More typedefs
60 //
61 typedef size_t size_type;
62
63
64 //
65 // Colors
66 //
67 enum color_type
68 {
69 RED,
70 BLACK
71 };
72
73
74 //
75 // A node of a tree
76 //
77 struct tree_node
78 {
79 value_type data;
80 tree_node * parent;
81 tree_node * left;
82 tree_node * right;
83 union {
84 color_type color;
85 size_type height;
86 };
87 };
88 typedef tree_node * node_ptr;
89 typedef node_ptr * link_type;
90
91
92 //
93 // Binary search tree base class
94 //
95 class binary_search_tree_base
96 {
97 protected:
98 // Data
99 node_ptr _root;
100
101 private:
102 // bst helper functions
103 void _clear( node_ptr );
104
105 protected:
106 // Links
107 typedef node_ptr * node_link;
108 link_type _make_link( node_ptr & );
109 link_type _get_parent_link( node_ptr );
110 node_ptr _link_dest( node_link );
111 void _link_set_dest( node_link, node_ptr );
112 node_ptr _link_origin( node_link );
113
114 protected:
115 // Node functions
116 color_type _node_color( node_ptr );
117 size_type _node_height( node_ptr );
118 node_ptr _node_sibling( node_ptr );
119 node_ptr _node_leftmost( node_ptr );
120
121 protected:
122 // Helper functions
123 void _rotate_clockwise( link_type node );
124 void _rotate_counterclockwise( link_type node );
125 pair<link_type, node_ptr> _get_insert_link(
126 const_reference );
127 node_ptr _replace_and_remove_node( node_ptr );
128
129 public:
130 // Interface
131 binary_search_tree_base();
132 ~binary_search_tree_base();
133 void clear();
134 node_ptr find( const_reference );
135 bool is_empty();
136 };
137
138
139 // Recursively clear the tree
140 void
141 binary_search_tree_base::_clear(
142 node_ptr node )
143 {
144 if( node ) {
145 _clear( node->left );
146 _clear( node->right );
147 delete node;
148 }
149 }
150
151
152 // Convert a node pointer to a link
153 link_type
154 binary_search_tree_base::_make_link(
155 node_ptr & node )
156 {
157 return & node;
158 }
159
160 // Get a node's parent's link to that node
161 link_type
162 binary_search_tree_base::_get_parent_link(
163 node_ptr node )
164 {
165 node_ptr parent = node->parent;
166 if( ! parent )
167 return _make_link( _root );
168 if( parent->left == node )
169 return _make_link( parent->left );
170 return _make_link( parent->right );
171 }
172
173 // Get a link's destination
174 node_ptr
175 binary_search_tree_base::_link_dest(
176 link_type link )
177 {
178 return *link;
179 }
180
181 // Set a link's destination
182 void
183 binary_search_tree_base::_link_set_dest(
184 link_type link, node_ptr dest )
185 {
186 *link = dest;
187 }
188
189 // Get a link's origin
190 node_ptr
191 binary_search_tree_base::_link_origin(
192 node_link link )
193 {
194 return _link_dest(link) -> parent;
195 }
196
197
198 // Get a node's color
199 // Also works for nonexistent imaginary black leaf nodes
200 color_type
201 binary_search_tree_base::_node_color(
202 node_ptr node )
203 {
204 if( node )
205 return node->color;
206 return BLACK;
207 }
208
209 // Get a node's height
210 size_type
211 binary_search_tree_base::_node_height(
212 node_ptr node )
213 {
214 if( node )
215 return node->height;
216 return 0;
217 }
218
219 // Get a node's sibling
220 node_ptr
221 binary_search_tree_base::_node_sibling(
222 node_ptr node )
223 {
224 node_ptr par = node->parent;
225 node_ptr sib = par->left;
226 if( sib == node )
227 sib = par->right;
228 return sib;
229 }
230
231 // Return the leftmost node of a subtree
232 node_ptr
233 binary_search_tree_base::_node_leftmost(
234 node_ptr node )
235 {
236 while( node->left )
237 node = node->left;
238 return node;
239 }
240
241
242 // Rotate about a node clockwise
243 void
244 binary_search_tree_base::_rotate_clockwise(
245 link_type link )
246 {
247 node_ptr node = _link_dest(link);
248 node_ptr left = node->left;
249 node_ptr lright = left->right;
250
251 _link_set_dest( link, left );
252 left->parent = node->parent;
253
254 left->right = node;
255 node->parent = left;
256
257 node->left = lright;
258 if( lright )
259 lright->parent = node;
260 }
261
262 // Rotate about a node counterclockwise
263 void
264 binary_search_tree_base::_rotate_counterclockwise(
265 link_type link )
266 {
267 node_ptr node = _link_dest(link);
268 node_ptr right = node->right;
269 node_ptr rleft = right->left;
270
271 _link_set_dest( link, right );
272 right->parent = node->parent;
273
274 right->left = node;
275 node->parent = right;
276
277 node->right = rleft;
278 if( rleft )
279 rleft->parent = node;
280 }
281
282 // Figure out where to insert a value into the tree
283 // Returns the link and its origin
284 pair<link_type, node_ptr>
285 binary_search_tree_base::_get_insert_link(
286 const_reference value )
287 {
288 link_type where = _make_link(_root);
289 node_ptr origin = nullptr;
290
291 while( _link_dest(where) ) {
292 origin = _link_dest(where);
293 if( value < origin->data )
294 where = _make_link( origin->left );
295 else if( value > origin->data )
296 where = _make_link( origin->right );
297 else {
298 where = nullptr;
299 break;
300 }
301 }
302
303 return make_pair( where, origin );
304 }
305
306 // Replace and remove node
307 // Returns actual node removed
308 node_ptr
309 binary_search_tree_base::_replace_and_remove_node(
310 node_ptr node )
311 {
312 node_ptr rep = nullptr;
313
314 if( node->left && node->right ) {
315 rep = _node_leftmost( node->right );
316 std::swap( rep->data, node->data );
317 node = rep;
318 rep = nullptr;
319 }
320
321 if( node->left )
322 rep = node->left;
323 else if( node->right )
324 rep = node->right;
325
326 _link_set_dest( _get_parent_link(node), rep );
327 if( rep )
328 rep->parent = node->parent;
329
330 return node;
331 }
332
333
334 // Constructor
335 binary_search_tree_base::binary_search_tree_base() :
336 _root( nullptr )
337 {
338 }
339
340 // Destructor
341 binary_search_tree_base::~binary_search_tree_base()
342 {
343 _clear( _root );
344 }
345
346 // Clear the tree
347 void
348 binary_search_tree_base::clear()
349 {
350 _clear( _root );
351 _root = nullptr;
352 }
353
354 // Find a value in the tree
355 node_ptr
356 binary_search_tree_base::find(
357 const_reference value )
358 {
359 node_ptr node = _root;
360 while( node ) {
361 if( value < node->data )
362 node = node->left;
363 else if( value > node->data )
364 node = node->right;
365 else
366 break;
367 }
368 return node;
369 }
370
371 // Is the tree empty?
372 bool
373 binary_search_tree_base::is_empty()
374 {
375 return _root == nullptr;
376 }
377
378
379
380 /*
381 * AVL tree
382 */
383
384
385 //
386 // AVL tree class
387 //
388 class avl_tree : public binary_search_tree_base
389 {
390 protected:
391 // Node functions
392 int _node_balance( node_ptr );
393 protected:
394 // Helper functions
395 bool _update_height( node_ptr );
396 bool _balance_node( node_ptr );
397 void _balance( node_ptr );
398 int _check( node_ptr );
399 public:
400 // Interface functions
401 void insert( const_reference );
402 void erase( const_reference );
403 void check();
404 };
405
406
407 // Balance of a node
408 int avl_tree::_node_balance( node_ptr node )
409 {
410 return _node_height(node->left)
411 - _node_height(node->right);
412 }
413
414
415 // Update the height of anode
416 // Returns true if height was changed
417 bool avl_tree::_update_height( node_ptr node )
418 {
419 if( ! node )
420 return false;
421 size_type oldH = node->height;
422 size_type leftH = _node_height( node->left );
423 size_type rightH = _node_height( node->right );
424 if( leftH > rightH )
425 node->height = leftH + 1;
426 else
427 node->height = rightH + 1;
428 return ! (oldH == node->height);
429 }
430
431 // Balance at node; return true if balance was done
432 bool avl_tree::_balance_node( node_ptr node )
433 {
434 int bal = _node_balance(node);
435 if( bal == 2 ) {
436 node_link link = _get_parent_link(node);
437 if( _node_balance(node->left) == -1 ) {
438 _rotate_counterclockwise(
439 _make_link(node->left) );
440 _update_height( node->left->left );
441 _update_height( node->left );
442 }
443 _rotate_clockwise( link );
444 _update_height( node );
445 _update_height( _link_dest(link) );
446 return true;
447 }
448 else if( bal == -2 ) {
449 node_link link = _get_parent_link(node);
450 if( _node_balance(node->right) == 1 ) {
451 _rotate_clockwise( _make_link(node->right) );
452 _update_height( node->right->right );
453 _update_height( node->right );
454 }
455 _rotate_counterclockwise( link );
456 _update_height( node );
457 _update_height( _link_dest(link) );
458 return true;
459 }
460 return false;
461 }
462
463 // Balance the tree
464 void avl_tree::_balance( node_ptr node )
465 {
466 while( node ) {
467 if( _balance_node(node) )
468 node = node->parent->parent;
469 else if( ! _update_height(node) )
470 break;
471 else
472 node = node->parent;
473 }
474 }
475
476 // Check the tree for validity
477 int avl_tree::_check( node_ptr root )
478 {
479
480 if( root == nullptr )
481 return 0;
482
483 node_ptr left = root->left;
484 node_ptr right = root->right;
485
486 // Don't trust nodes' height values
487 int lh = _check(left);
488 int rh = _check(right);
489
490 // Make sure balance factor is not +2 or -2
491 if( lh - rh >= +2 )
492 throw "Right heavy!";
493 else if( lh - rh <= -2 )
494 throw "Left heavy!";
495
496 // Make sure left < right
497 if( left ) {
498 if( left->data > root->data )
499 throw "Not bst!";
500 }
501 if( right ) {
502 if( right->data < root->data )
503 throw "Not bst!";
504 }
505
506 // Make sure child->parent points to here
507 if( left )
508 if( left->parent != root )
509 throw "plink wrong!";
510 if( right )
511 if( right->parent != root )
512 throw "plink wrong!";
513
514 // Calculate height
515 int height = lh;
516 if( height < rh ) height = rh;
517 ++ height;
518
519 // Return results
520 return height;
521 }
522
523
524 // Insert data into the tree
525 void avl_tree::insert( const_reference value )
526 {
527 pair<link_type, node_ptr> where;
528 where = _get_insert_link( value );
529 if( ! where.first )
530 return;
531
532 node_ptr node = new tree_node;
533 node->data = value;
534 node->height = 1;
535 node->left = node->right = nullptr;
536 node->parent = where.second;
537 _link_set_dest( where.first, node );
538
539 _balance( node->parent );
540 }
541
542 // Erase data from the tree
543 void avl_tree::erase( const_reference value )
544 {
545 node_ptr node = find( value );
546 if( ! node )
547 return;
548 node = _replace_and_remove_node( node );
549 _balance( node->parent );
550 delete node;
551 }
552
553 // Make sure that the tree is valid
554 void avl_tree::check()
555 {
556 _check( _root );
557 }
558
559
560
561 /*
562 * Red-black tree
563 */
564
565 //
566 // Red-black tree class
567 //
568 class rb_tree : public binary_search_tree_base
569 {
570 protected:
571 // Helper functions
572 void _insert_harder_balancing( node_ptr );
573 void _insert_balance( node_ptr );
574 void _erase_harder_balancing_red_parent( node_ptr );
575 void _erase_harder_balancing( node_ptr );
576 void _erase_balance( node_ptr );
577 int _check( node_ptr );
578 public:
579 // Interface functions
580 void insert( const_reference );
581 void erase( const_reference );
582 void check();
583 };
584
585
586 // Balances tree after insertion; handle harder cases
587 void
588 rb_tree::_insert_harder_balancing(
589 node_ptr node )
590 {
591 node_ptr parent = node->parent;
592 node_ptr sibling = _node_sibling( node );
593
594 if( _node_color(sibling) == RED ) {
595 sibling->color = BLACK;
596 parent->color = RED;
597 if( _node_color(parent->parent) == RED ) {
598 parent->parent->color = BLACK;
599 _insert_harder_balancing( parent->parent );
600 }
601 }
602
603 else {
604 parent->color = RED;
605 if( node == parent->left ) {
606 if( _node_color(node->right) == RED ) {
607 node->color = RED;
608 node->right->color = BLACK;
609 _rotate_counterclockwise(
610 _make_link(parent->left) );
611 }
612 _rotate_clockwise(
613 _get_parent_link(parent) );
614 }
615 else {
616 if( _node_color(node->left) == RED ) {
617 node->color = RED;
618 node->left->color = BLACK;
619 _rotate_clockwise(
620 _make_link(parent->right) );
621 }
622 _rotate_counterclockwise(
623 _get_parent_link(parent) );
624 }
625 }
626 }
627
628 // Balance the tree after insertion; easy cases
629 void
630 rb_tree::_insert_balance(
631 node_ptr node )
632 {
633 if( ! node->parent ) {
634 node->color = BLACK;
635 }
636 else if( node->parent->color != BLACK ) {
637 node->parent->color = BLACK;
638 _insert_harder_balancing( node->parent );
639 _root->color = BLACK;
640 }
641 }
642
643 // Balance after erasing; harder; red parent
644 void
645 rb_tree::_erase_harder_balancing_red_parent(
646 node_ptr sibling )
647 {
648 node_ptr parent = sibling->parent;
649 if( sibling == parent->right ) {
650 if( _node_color(sibling->left) == RED ) {
651 parent->color = BLACK;
652 _rotate_clockwise( _make_link(parent->right) );
653 }
654 _rotate_counterclockwise( _get_parent_link(parent) );
655 }
656 else {
657 if( _node_color(sibling->right) == RED ) {
658 parent->color = BLACK;
659 _rotate_counterclockwise(
660 _make_link(parent->left) );
661 }
662 _rotate_clockwise( _get_parent_link(parent) );
663 }
664 }
665
666 // Balance after erasing; harder
667 void
668 rb_tree::_erase_harder_balancing(
669 node_ptr sibling )
670 {
671 // Use default rotations and sib->left|right
672 typedef void (rb_tree::*rot_func)( link_type );
673 rot_func rot_clock = & rb_tree::_rotate_clockwise;
674 rot_func rot_counter=& rb_tree::_rotate_counterclockwise;
675 node_ptr sib_left = sibling->left;
676 node_ptr sib_right = sibling->right;
677
678 // One more variable
679 node_ptr parent = sibling->parent;
680
681 // But if sibling is on the left, turn them around.
682 if( sibling == parent->left ) {
683 swap( rot_clock, rot_counter );
684 swap( sib_left, sib_right );
685 }
686
687 if( _node_color(parent) == BLACK ) {
688 if( sibling->color == BLACK ) {
689 if( _node_color(sib_left) == BLACK
690 && _node_color(sib_right) == BLACK ) {
691 sibling->color = RED;
692 if( parent->parent ) {
693 _erase_harder_balancing(
694 _node_sibling(parent) );
695 }
696 }
697 else {
698 if( _node_color(sib_left) == RED ) {
699 sib_left->color = BLACK;
700 (this->*rot_clock)(
701 _get_parent_link(sibling) );
702 }
703 else
704 sib_right->color = BLACK;
705 (this->*rot_counter)(
706 _get_parent_link(parent) );
707 }
708 }
709 else {
710 parent->color = RED;
711 sibling->color = BLACK;
712 (this->*rot_counter)( _get_parent_link(parent) );
713 _erase_harder_balancing_red_parent( sib_left );
714 }
715 }
716 else {
717 _erase_harder_balancing_red_parent( sibling );
718 }
719 }
720
721 // Balance the tree after erasing
722 void
723 rb_tree::_erase_balance(
724 node_ptr node )
725 {
726 if( _node_color(node) == BLACK ) {
727 if( node->left )
728 node->left->color = BLACK;
729 else if( node->right )
730 node->right->color = BLACK;
731 else if( node->parent ) {
732 node_ptr sib = node->parent->left;
733 if( ! sib )
734 sib = node->parent->right;
735 _erase_harder_balancing( sib );
736 }
737 }
738 }
739
740 // Check for correctness
741 int
742 rb_tree::_check(
743 node_ptr subtree )
744 {
745 // Imaginary leaf? black height is 1
746 if( ! subtree )
747 return 1;
748
749 node_ptr left = subtree->left;
750 node_ptr right = subtree->right;
751
752 // Black height of both sides must be the same
753 int left_height = _check( left );
754 int right_height = _check( right );
755 if( left_height != right_height )
756 throw "black imbalance!";
757
758 // No two reds in a row
759 if( _node_color(subtree) == RED ) {
760 if( _node_color(left) == RED
761 || _node_color(right) == RED )
762 throw "two reds in a row!";
763 }
764
765 // We're black, the height is [left|right]_height + 1
766 else
767 ++ left_height;
768
769 // Make sure that the children's parents are correct
770 if( (left && left->parent != subtree)
771 || (right && right->parent != subtree) )
772 throw "parent pointer wrong!";
773
774 // Return our height
775 return left_height;
776 }
777
778
779 // Insert data into the tree
780 void rb_tree::insert( const_reference value )
781 {
782 pair<link_type, node_ptr> where;
783 where = _get_insert_link( value );
784 if( ! where.first )
785 return;
786
787 node_ptr node = new tree_node;
788 node->data = value;
789 node->color = RED;
790 node->left = node->right = nullptr;
791 node->parent = where.second;
792 _link_set_dest( where.first, node );
793
794 _insert_balance( node );
795 }
796
797 // Erase data from the tree
798 void rb_tree::erase( const_reference value )
799 {
800 node_ptr node = find( value );
801 if( ! node )
802 return;
803 node = _replace_and_remove_node( node );
804 _erase_balance( node );
805 delete node;
806 }
807
808 // Make sure that the tree is valid
809 void rb_tree::check()
810 {
811 if( _node_color(_root) == RED )
812 throw "root is red!";
813 _check( _root );
814 }
815
816
817
818 /*
819 * Random number generation
820 */
821
822
823 //
824 // Random number generator class
825 // It always returns the exact same sequence after being
826 // reset. Also, it never returns a number twice.
827 // Max: SQRT_RAND_MAX
828 // Max Value: SQRT_RAND_MAX*SQRT_RAND_MAX - 1
829 //
830 class random_generator
831 {
832 int * _numbers;
833 int _major_index;
834 int _minor_index;
835 public:
836 // Interface
837 random_generator();
838 ~random_generator();
839 int get();
840 void reset();
841 }
842 random_number;
843
844
845 // Construct a random number generator
846 random_generator::random_generator() :
847 _numbers( new int[SQRT_RAND_MAX] ),
848 _major_index( 0 ),
849 _minor_index( 0 )
850 {
851 for( int i = 0; i != SQRT_RAND_MAX; ++i )
852 _numbers[i] = rand() % SQRT_RAND_MAX;
853 }
854
855 // Destructor
856 random_generator::~random_generator()
857 {
858 delete[] _numbers;
859 }
860
861 // Get a random number
862 int random_generator::get()
863 {
864 int ret = _numbers[_major_index] * SQRT_RAND_MAX
865 + _numbers[_minor_index];
866 ++ _major_index;
867 if( _major_index == SQRT_RAND_MAX ) {
868 _major_index = 0;
869 ++ _minor_index;
870 }
871 return ret;
872 }
873
874 // Reset the generator
875 void random_generator::reset()
876 {
877 _major_index = 0;
878 _minor_index = 0;
879 }
880
881
882
883 /*
884 * Timer
885 */
886
887
888 //
889 // Timer class
890 //
891 class code_timer
892 {
893 // Data
894 chrono::high_resolution_clock::time_point _t;
895 public:
896 void start();
897 void stop();
898 };
899
900
901 // Start the timer
902 void code_timer::start()
903 {
904 _t = chrono::high_resolution_clock::now();
905 }
906
907 // Stop the timer
908 void code_timer::stop()
909 {
910 chrono::microseconds ns
911 = chrono::high_resolution_clock::now() - _t;
912 cout << ns.count() << " microseconds" << endl;
913 }
914
915
916
917 /*
918 * Testing
919 */
920
921 //
922 // Test a tree
923 //
924 template<typename Tree>
925 void test( int count )
926 {
927 Tree tree;
928 code_timer timer;
929 int i;
930
931 cout << "--- Inserting in order ---" << endl;
932 timer.start();
933 for( i = 0; i != count; ++i ) {
934 tree.insert( i );
935 // tree.check();
936 }
937 timer.stop();
938
939 cout << "--- Searching all afterwards ---" << endl;
940 timer.start();
941 for( i = 0; i != count; ++i ) {
942 tree.find( i );
943 }
944 timer.stop();
945
946 cout << "--- Erasing in order ---" << endl;
947 timer.start();
948 for( i = 0; i != count; ++i ) {
949 tree.erase( i );
950 // tree.check();
951 }
952 timer.stop();
953 /*
954 cout << "--- Inserting in random ---" << endl;
955 random_number.reset();
956 timer.start();
957 for( i = 0; i != count; ++i ) {
958 tree.insert( random_number.get() );
959 // tree.check();
960 }
961 timer.stop();
962
963 cout << "--- Searching all afterwards ---" << endl;
964 random_number.reset();
965 timer.start();
966 for( i = 0; i != count; ++i ) {
967 tree.find( random_number.get() );
968 }
969 timer.stop();
970
971 cout << "--- Erasing in random ---" << endl;
972 random_number.reset();
973 timer.start();
974 for( i = 0; i != count; ++i ) {
975 tree.erase( random_number.get() );
976 // tree.check();
977 }
978 timer.stop();*/
979 }
980
981
982
983 /*
984 * Main function
985 */
986
987 int main()
988 {
989 try {
990 code_timer timer;
991 timer.start();
992 for( int i = INTERVAL; i < INTERVAL*10; i += INTERVAL ) {
993 cout << endl << "AVL TREE, " << i << endl;
994 test<avl_tree>( i );
995 cout << endl << "RED-BLACK TREE, " << i << endl;
996 test<rb_tree>( i );
997 }
998 cout << endl << "TOTAL PROGRAM RUNNING TIME:" << endl;
999 timer.stop();
1000 }
1001 catch( const char * e ) {
1002 cout << e << endl;
1003 }
1004 };
1005
2 // AVL versus Red-Black
3 // written by Nathan Belue
4 // April 30, 2012 - May 1, 2012
5 //
6 // This is a comparison of AVL trees to red-black trees. It
7 // tests various scenarios and the speed of each tree in each
8 // scenario.
9 //
10
11
12 /*
13 * Included files
14 */
15
16 #include <chrono>
17 #include <cstdlib>
18 #include <iostream>
19
20 using namespace std;
21
22
23
24 /*
25 * Constants
26 */
27
28
29 namespace {
30
31 //
32 // Set this to how big of intervals to step
33 //
34 const int INTERVAL = 1000000;
35
36
37 //
38 // The square root of the maximum random value
39 //
40 const int SQRT_RAND_MAX = 1000000;
41
42 }
43
44
45
46 /*
47 * Binary search tree base
48 */
49
50
51 //
52 // Value being stored
53 //
54 typedef int value_type;
55 typedef const value_type & const_reference;
56
57
58 //
59 // More typedefs
60 //
61 typedef size_t size_type;
62
63
64 //
65 // Colors
66 //
67 enum color_type
68 {
69 RED,
70 BLACK
71 };
72
73
74 //
75 // A node of a tree
76 //
77 struct tree_node
78 {
79 value_type data;
80 tree_node * parent;
81 tree_node * left;
82 tree_node * right;
83 union {
84 color_type color;
85 size_type height;
86 };
87 };
88 typedef tree_node * node_ptr;
89 typedef node_ptr * link_type;
90
91
92 //
93 // Binary search tree base class
94 //
95 class binary_search_tree_base
96 {
97 protected:
98 // Data
99 node_ptr _root;
100
101 private:
102 // bst helper functions
103 void _clear( node_ptr );
104
105 protected:
106 // Links
107 typedef node_ptr * node_link;
108 link_type _make_link( node_ptr & );
109 link_type _get_parent_link( node_ptr );
110 node_ptr _link_dest( node_link );
111 void _link_set_dest( node_link, node_ptr );
112 node_ptr _link_origin( node_link );
113
114 protected:
115 // Node functions
116 color_type _node_color( node_ptr );
117 size_type _node_height( node_ptr );
118 node_ptr _node_sibling( node_ptr );
119 node_ptr _node_leftmost( node_ptr );
120
121 protected:
122 // Helper functions
123 void _rotate_clockwise( link_type node );
124 void _rotate_counterclockwise( link_type node );
125 pair<link_type, node_ptr> _get_insert_link(
126 const_reference );
127 node_ptr _replace_and_remove_node( node_ptr );
128
129 public:
130 // Interface
131 binary_search_tree_base();
132 ~binary_search_tree_base();
133 void clear();
134 node_ptr find( const_reference );
135 bool is_empty();
136 };
137
138
139 // Recursively clear the tree
140 void
141 binary_search_tree_base::_clear(
142 node_ptr node )
143 {
144 if( node ) {
145 _clear( node->left );
146 _clear( node->right );
147 delete node;
148 }
149 }
150
151
152 // Convert a node pointer to a link
153 link_type
154 binary_search_tree_base::_make_link(
155 node_ptr & node )
156 {
157 return & node;
158 }
159
160 // Get a node's parent's link to that node
161 link_type
162 binary_search_tree_base::_get_parent_link(
163 node_ptr node )
164 {
165 node_ptr parent = node->parent;
166 if( ! parent )
167 return _make_link( _root );
168 if( parent->left == node )
169 return _make_link( parent->left );
170 return _make_link( parent->right );
171 }
172
173 // Get a link's destination
174 node_ptr
175 binary_search_tree_base::_link_dest(
176 link_type link )
177 {
178 return *link;
179 }
180
181 // Set a link's destination
182 void
183 binary_search_tree_base::_link_set_dest(
184 link_type link, node_ptr dest )
185 {
186 *link = dest;
187 }
188
189 // Get a link's origin
190 node_ptr
191 binary_search_tree_base::_link_origin(
192 node_link link )
193 {
194 return _link_dest(link) -> parent;
195 }
196
197
198 // Get a node's color
199 // Also works for nonexistent imaginary black leaf nodes
200 color_type
201 binary_search_tree_base::_node_color(
202 node_ptr node )
203 {
204 if( node )
205 return node->color;
206 return BLACK;
207 }
208
209 // Get a node's height
210 size_type
211 binary_search_tree_base::_node_height(
212 node_ptr node )
213 {
214 if( node )
215 return node->height;
216 return 0;
217 }
218
219 // Get a node's sibling
220 node_ptr
221 binary_search_tree_base::_node_sibling(
222 node_ptr node )
223 {
224 node_ptr par = node->parent;
225 node_ptr sib = par->left;
226 if( sib == node )
227 sib = par->right;
228 return sib;
229 }
230
231 // Return the leftmost node of a subtree
232 node_ptr
233 binary_search_tree_base::_node_leftmost(
234 node_ptr node )
235 {
236 while( node->left )
237 node = node->left;
238 return node;
239 }
240
241
242 // Rotate about a node clockwise
243 void
244 binary_search_tree_base::_rotate_clockwise(
245 link_type link )
246 {
247 node_ptr node = _link_dest(link);
248 node_ptr left = node->left;
249 node_ptr lright = left->right;
250
251 _link_set_dest( link, left );
252 left->parent = node->parent;
253
254 left->right = node;
255 node->parent = left;
256
257 node->left = lright;
258 if( lright )
259 lright->parent = node;
260 }
261
262 // Rotate about a node counterclockwise
263 void
264 binary_search_tree_base::_rotate_counterclockwise(
265 link_type link )
266 {
267 node_ptr node = _link_dest(link);
268 node_ptr right = node->right;
269 node_ptr rleft = right->left;
270
271 _link_set_dest( link, right );
272 right->parent = node->parent;
273
274 right->left = node;
275 node->parent = right;
276
277 node->right = rleft;
278 if( rleft )
279 rleft->parent = node;
280 }
281
282 // Figure out where to insert a value into the tree
283 // Returns the link and its origin
284 pair<link_type, node_ptr>
285 binary_search_tree_base::_get_insert_link(
286 const_reference value )
287 {
288 link_type where = _make_link(_root);
289 node_ptr origin = nullptr;
290
291 while( _link_dest(where) ) {
292 origin = _link_dest(where);
293 if( value < origin->data )
294 where = _make_link( origin->left );
295 else if( value > origin->data )
296 where = _make_link( origin->right );
297 else {
298 where = nullptr;
299 break;
300 }
301 }
302
303 return make_pair( where, origin );
304 }
305
306 // Replace and remove node
307 // Returns actual node removed
308 node_ptr
309 binary_search_tree_base::_replace_and_remove_node(
310 node_ptr node )
311 {
312 node_ptr rep = nullptr;
313
314 if( node->left && node->right ) {
315 rep = _node_leftmost( node->right );
316 std::swap( rep->data, node->data );
317 node = rep;
318 rep = nullptr;
319 }
320
321 if( node->left )
322 rep = node->left;
323 else if( node->right )
324 rep = node->right;
325
326 _link_set_dest( _get_parent_link(node), rep );
327 if( rep )
328 rep->parent = node->parent;
329
330 return node;
331 }
332
333
334 // Constructor
335 binary_search_tree_base::binary_search_tree_base() :
336 _root( nullptr )
337 {
338 }
339
340 // Destructor
341 binary_search_tree_base::~binary_search_tree_base()
342 {
343 _clear( _root );
344 }
345
346 // Clear the tree
347 void
348 binary_search_tree_base::clear()
349 {
350 _clear( _root );
351 _root = nullptr;
352 }
353
354 // Find a value in the tree
355 node_ptr
356 binary_search_tree_base::find(
357 const_reference value )
358 {
359 node_ptr node = _root;
360 while( node ) {
361 if( value < node->data )
362 node = node->left;
363 else if( value > node->data )
364 node = node->right;
365 else
366 break;
367 }
368 return node;
369 }
370
371 // Is the tree empty?
372 bool
373 binary_search_tree_base::is_empty()
374 {
375 return _root == nullptr;
376 }
377
378
379
380 /*
381 * AVL tree
382 */
383
384
385 //
386 // AVL tree class
387 //
388 class avl_tree : public binary_search_tree_base
389 {
390 protected:
391 // Node functions
392 int _node_balance( node_ptr );
393 protected:
394 // Helper functions
395 bool _update_height( node_ptr );
396 bool _balance_node( node_ptr );
397 void _balance( node_ptr );
398 int _check( node_ptr );
399 public:
400 // Interface functions
401 void insert( const_reference );
402 void erase( const_reference );
403 void check();
404 };
405
406
407 // Balance of a node
408 int avl_tree::_node_balance( node_ptr node )
409 {
410 return _node_height(node->left)
411 - _node_height(node->right);
412 }
413
414
415 // Update the height of anode
416 // Returns true if height was changed
417 bool avl_tree::_update_height( node_ptr node )
418 {
419 if( ! node )
420 return false;
421 size_type oldH = node->height;
422 size_type leftH = _node_height( node->left );
423 size_type rightH = _node_height( node->right );
424 if( leftH > rightH )
425 node->height = leftH + 1;
426 else
427 node->height = rightH + 1;
428 return ! (oldH == node->height);
429 }
430
431 // Balance at node; return true if balance was done
432 bool avl_tree::_balance_node( node_ptr node )
433 {
434 int bal = _node_balance(node);
435 if( bal == 2 ) {
436 node_link link = _get_parent_link(node);
437 if( _node_balance(node->left) == -1 ) {
438 _rotate_counterclockwise(
439 _make_link(node->left) );
440 _update_height( node->left->left );
441 _update_height( node->left );
442 }
443 _rotate_clockwise( link );
444 _update_height( node );
445 _update_height( _link_dest(link) );
446 return true;
447 }
448 else if( bal == -2 ) {
449 node_link link = _get_parent_link(node);
450 if( _node_balance(node->right) == 1 ) {
451 _rotate_clockwise( _make_link(node->right) );
452 _update_height( node->right->right );
453 _update_height( node->right );
454 }
455 _rotate_counterclockwise( link );
456 _update_height( node );
457 _update_height( _link_dest(link) );
458 return true;
459 }
460 return false;
461 }
462
463 // Balance the tree
464 void avl_tree::_balance( node_ptr node )
465 {
466 while( node ) {
467 if( _balance_node(node) )
468 node = node->parent->parent;
469 else if( ! _update_height(node) )
470 break;
471 else
472 node = node->parent;
473 }
474 }
475
476 // Check the tree for validity
477 int avl_tree::_check( node_ptr root )
478 {
479
480 if( root == nullptr )
481 return 0;
482
483 node_ptr left = root->left;
484 node_ptr right = root->right;
485
486 // Don't trust nodes' height values
487 int lh = _check(left);
488 int rh = _check(right);
489
490 // Make sure balance factor is not +2 or -2
491 if( lh - rh >= +2 )
492 throw "Right heavy!";
493 else if( lh - rh <= -2 )
494 throw "Left heavy!";
495
496 // Make sure left < right
497 if( left ) {
498 if( left->data > root->data )
499 throw "Not bst!";
500 }
501 if( right ) {
502 if( right->data < root->data )
503 throw "Not bst!";
504 }
505
506 // Make sure child->parent points to here
507 if( left )
508 if( left->parent != root )
509 throw "plink wrong!";
510 if( right )
511 if( right->parent != root )
512 throw "plink wrong!";
513
514 // Calculate height
515 int height = lh;
516 if( height < rh ) height = rh;
517 ++ height;
518
519 // Return results
520 return height;
521 }
522
523
524 // Insert data into the tree
525 void avl_tree::insert( const_reference value )
526 {
527 pair<link_type, node_ptr> where;
528 where = _get_insert_link( value );
529 if( ! where.first )
530 return;
531
532 node_ptr node = new tree_node;
533 node->data = value;
534 node->height = 1;
535 node->left = node->right = nullptr;
536 node->parent = where.second;
537 _link_set_dest( where.first, node );
538
539 _balance( node->parent );
540 }
541
542 // Erase data from the tree
543 void avl_tree::erase( const_reference value )
544 {
545 node_ptr node = find( value );
546 if( ! node )
547 return;
548 node = _replace_and_remove_node( node );
549 _balance( node->parent );
550 delete node;
551 }
552
553 // Make sure that the tree is valid
554 void avl_tree::check()
555 {
556 _check( _root );
557 }
558
559
560
561 /*
562 * Red-black tree
563 */
564
565 //
566 // Red-black tree class
567 //
568 class rb_tree : public binary_search_tree_base
569 {
570 protected:
571 // Helper functions
572 void _insert_harder_balancing( node_ptr );
573 void _insert_balance( node_ptr );
574 void _erase_harder_balancing_red_parent( node_ptr );
575 void _erase_harder_balancing( node_ptr );
576 void _erase_balance( node_ptr );
577 int _check( node_ptr );
578 public:
579 // Interface functions
580 void insert( const_reference );
581 void erase( const_reference );
582 void check();
583 };
584
585
586 // Balances tree after insertion; handle harder cases
587 void
588 rb_tree::_insert_harder_balancing(
589 node_ptr node )
590 {
591 node_ptr parent = node->parent;
592 node_ptr sibling = _node_sibling( node );
593
594 if( _node_color(sibling) == RED ) {
595 sibling->color = BLACK;
596 parent->color = RED;
597 if( _node_color(parent->parent) == RED ) {
598 parent->parent->color = BLACK;
599 _insert_harder_balancing( parent->parent );
600 }
601 }
602
603 else {
604 parent->color = RED;
605 if( node == parent->left ) {
606 if( _node_color(node->right) == RED ) {
607 node->color = RED;
608 node->right->color = BLACK;
609 _rotate_counterclockwise(
610 _make_link(parent->left) );
611 }
612 _rotate_clockwise(
613 _get_parent_link(parent) );
614 }
615 else {
616 if( _node_color(node->left) == RED ) {
617 node->color = RED;
618 node->left->color = BLACK;
619 _rotate_clockwise(
620 _make_link(parent->right) );
621 }
622 _rotate_counterclockwise(
623 _get_parent_link(parent) );
624 }
625 }
626 }
627
628 // Balance the tree after insertion; easy cases
629 void
630 rb_tree::_insert_balance(
631 node_ptr node )
632 {
633 if( ! node->parent ) {
634 node->color = BLACK;
635 }
636 else if( node->parent->color != BLACK ) {
637 node->parent->color = BLACK;
638 _insert_harder_balancing( node->parent );
639 _root->color = BLACK;
640 }
641 }
642
643 // Balance after erasing; harder; red parent
644 void
645 rb_tree::_erase_harder_balancing_red_parent(
646 node_ptr sibling )
647 {
648 node_ptr parent = sibling->parent;
649 if( sibling == parent->right ) {
650 if( _node_color(sibling->left) == RED ) {
651 parent->color = BLACK;
652 _rotate_clockwise( _make_link(parent->right) );
653 }
654 _rotate_counterclockwise( _get_parent_link(parent) );
655 }
656 else {
657 if( _node_color(sibling->right) == RED ) {
658 parent->color = BLACK;
659 _rotate_counterclockwise(
660 _make_link(parent->left) );
661 }
662 _rotate_clockwise( _get_parent_link(parent) );
663 }
664 }
665
666 // Balance after erasing; harder
667 void
668 rb_tree::_erase_harder_balancing(
669 node_ptr sibling )
670 {
671 // Use default rotations and sib->left|right
672 typedef void (rb_tree::*rot_func)( link_type );
673 rot_func rot_clock = & rb_tree::_rotate_clockwise;
674 rot_func rot_counter=& rb_tree::_rotate_counterclockwise;
675 node_ptr sib_left = sibling->left;
676 node_ptr sib_right = sibling->right;
677
678 // One more variable
679 node_ptr parent = sibling->parent;
680
681 // But if sibling is on the left, turn them around.
682 if( sibling == parent->left ) {
683 swap( rot_clock, rot_counter );
684 swap( sib_left, sib_right );
685 }
686
687 if( _node_color(parent) == BLACK ) {
688 if( sibling->color == BLACK ) {
689 if( _node_color(sib_left) == BLACK
690 && _node_color(sib_right) == BLACK ) {
691 sibling->color = RED;
692 if( parent->parent ) {
693 _erase_harder_balancing(
694 _node_sibling(parent) );
695 }
696 }
697 else {
698 if( _node_color(sib_left) == RED ) {
699 sib_left->color = BLACK;
700 (this->*rot_clock)(
701 _get_parent_link(sibling) );
702 }
703 else
704 sib_right->color = BLACK;
705 (this->*rot_counter)(
706 _get_parent_link(parent) );
707 }
708 }
709 else {
710 parent->color = RED;
711 sibling->color = BLACK;
712 (this->*rot_counter)( _get_parent_link(parent) );
713 _erase_harder_balancing_red_parent( sib_left );
714 }
715 }
716 else {
717 _erase_harder_balancing_red_parent( sibling );
718 }
719 }
720
721 // Balance the tree after erasing
722 void
723 rb_tree::_erase_balance(
724 node_ptr node )
725 {
726 if( _node_color(node) == BLACK ) {
727 if( node->left )
728 node->left->color = BLACK;
729 else if( node->right )
730 node->right->color = BLACK;
731 else if( node->parent ) {
732 node_ptr sib = node->parent->left;
733 if( ! sib )
734 sib = node->parent->right;
735 _erase_harder_balancing( sib );
736 }
737 }
738 }
739
740 // Check for correctness
741 int
742 rb_tree::_check(
743 node_ptr subtree )
744 {
745 // Imaginary leaf? black height is 1
746 if( ! subtree )
747 return 1;
748
749 node_ptr left = subtree->left;
750 node_ptr right = subtree->right;
751
752 // Black height of both sides must be the same
753 int left_height = _check( left );
754 int right_height = _check( right );
755 if( left_height != right_height )
756 throw "black imbalance!";
757
758 // No two reds in a row
759 if( _node_color(subtree) == RED ) {
760 if( _node_color(left) == RED
761 || _node_color(right) == RED )
762 throw "two reds in a row!";
763 }
764
765 // We're black, the height is [left|right]_height + 1
766 else
767 ++ left_height;
768
769 // Make sure that the children's parents are correct
770 if( (left && left->parent != subtree)
771 || (right && right->parent != subtree) )
772 throw "parent pointer wrong!";
773
774 // Return our height
775 return left_height;
776 }
777
778
779 // Insert data into the tree
780 void rb_tree::insert( const_reference value )
781 {
782 pair<link_type, node_ptr> where;
783 where = _get_insert_link( value );
784 if( ! where.first )
785 return;
786
787 node_ptr node = new tree_node;
788 node->data = value;
789 node->color = RED;
790 node->left = node->right = nullptr;
791 node->parent = where.second;
792 _link_set_dest( where.first, node );
793
794 _insert_balance( node );
795 }
796
797 // Erase data from the tree
798 void rb_tree::erase( const_reference value )
799 {
800 node_ptr node = find( value );
801 if( ! node )
802 return;
803 node = _replace_and_remove_node( node );
804 _erase_balance( node );
805 delete node;
806 }
807
808 // Make sure that the tree is valid
809 void rb_tree::check()
810 {
811 if( _node_color(_root) == RED )
812 throw "root is red!";
813 _check( _root );
814 }
815
816
817
818 /*
819 * Random number generation
820 */
821
822
823 //
824 // Random number generator class
825 // It always returns the exact same sequence after being
826 // reset. Also, it never returns a number twice.
827 // Max: SQRT_RAND_MAX
828 // Max Value: SQRT_RAND_MAX*SQRT_RAND_MAX - 1
829 //
830 class random_generator
831 {
832 int * _numbers;
833 int _major_index;
834 int _minor_index;
835 public:
836 // Interface
837 random_generator();
838 ~random_generator();
839 int get();
840 void reset();
841 }
842 random_number;
843
844
845 // Construct a random number generator
846 random_generator::random_generator() :
847 _numbers( new int[SQRT_RAND_MAX] ),
848 _major_index( 0 ),
849 _minor_index( 0 )
850 {
851 for( int i = 0; i != SQRT_RAND_MAX; ++i )
852 _numbers[i] = rand() % SQRT_RAND_MAX;
853 }
854
855 // Destructor
856 random_generator::~random_generator()
857 {
858 delete[] _numbers;
859 }
860
861 // Get a random number
862 int random_generator::get()
863 {
864 int ret = _numbers[_major_index] * SQRT_RAND_MAX
865 + _numbers[_minor_index];
866 ++ _major_index;
867 if( _major_index == SQRT_RAND_MAX ) {
868 _major_index = 0;
869 ++ _minor_index;
870 }
871 return ret;
872 }
873
874 // Reset the generator
875 void random_generator::reset()
876 {
877 _major_index = 0;
878 _minor_index = 0;
879 }
880
881
882
883 /*
884 * Timer
885 */
886
887
888 //
889 // Timer class
890 //
891 class code_timer
892 {
893 // Data
894 chrono::high_resolution_clock::time_point _t;
895 public:
896 void start();
897 void stop();
898 };
899
900
901 // Start the timer
902 void code_timer::start()
903 {
904 _t = chrono::high_resolution_clock::now();
905 }
906
907 // Stop the timer
908 void code_timer::stop()
909 {
910 chrono::microseconds ns
911 = chrono::high_resolution_clock::now() - _t;
912 cout << ns.count() << " microseconds" << endl;
913 }
914
915
916
917 /*
918 * Testing
919 */
920
921 //
922 // Test a tree
923 //
924 template<typename Tree>
925 void test( int count )
926 {
927 Tree tree;
928 code_timer timer;
929 int i;
930
931 cout << "--- Inserting in order ---" << endl;
932 timer.start();
933 for( i = 0; i != count; ++i ) {
934 tree.insert( i );
935 // tree.check();
936 }
937 timer.stop();
938
939 cout << "--- Searching all afterwards ---" << endl;
940 timer.start();
941 for( i = 0; i != count; ++i ) {
942 tree.find( i );
943 }
944 timer.stop();
945
946 cout << "--- Erasing in order ---" << endl;
947 timer.start();
948 for( i = 0; i != count; ++i ) {
949 tree.erase( i );
950 // tree.check();
951 }
952 timer.stop();
953 /*
954 cout << "--- Inserting in random ---" << endl;
955 random_number.reset();
956 timer.start();
957 for( i = 0; i != count; ++i ) {
958 tree.insert( random_number.get() );
959 // tree.check();
960 }
961 timer.stop();
962
963 cout << "--- Searching all afterwards ---" << endl;
964 random_number.reset();
965 timer.start();
966 for( i = 0; i != count; ++i ) {
967 tree.find( random_number.get() );
968 }
969 timer.stop();
970
971 cout << "--- Erasing in random ---" << endl;
972 random_number.reset();
973 timer.start();
974 for( i = 0; i != count; ++i ) {
975 tree.erase( random_number.get() );
976 // tree.check();
977 }
978 timer.stop();*/
979 }
980
981
982
983 /*
984 * Main function
985 */
986
987 int main()
988 {
989 try {
990 code_timer timer;
991 timer.start();
992 for( int i = INTERVAL; i < INTERVAL*10; i += INTERVAL ) {
993 cout << endl << "AVL TREE, " << i << endl;
994 test<avl_tree>( i );
995 cout << endl << "RED-BLACK TREE, " << i << endl;
996 test<rb_tree>( i );
997 }
998 cout << endl << "TOTAL PROGRAM RUNNING TIME:" << endl;
999 timer.stop();
1000 }
1001 catch( const char * e ) {
1002 cout << e << endl;
1003 }
1004 };
1005
Results:
The output:
AVL TREE, 1000000
--- Inserting in order ---
212706 microseconds
--- Searching all afterwards ---
118500 microseconds
--- Erasing in order ---
144221 microseconds
RED-BLACK TREE, 1000000
--- Inserting in order ---
253050 microseconds
--- Searching all afterwards ---
128795 microseconds
--- Erasing in order ---
177536 microseconds
AVL TREE, 2000000
--- Inserting in order ---
444879 microseconds
--- Searching all afterwards ---
297211 microseconds
--- Erasing in order ---
324151 microseconds
RED-BLACK TREE, 2000000
--- Inserting in order ---
578794 microseconds
--- Searching all afterwards ---
308073 microseconds
--- Erasing in order ---
394074 microseconds
AVL TREE, 3000000
--- Inserting in order ---
697510 microseconds
--- Searching all afterwards ---
466717 microseconds
--- Erasing in order ---
510471 microseconds
RED-BLACK TREE, 3000000
--- Inserting in order ---
901346 microseconds
--- Searching all afterwards ---
502883 microseconds
--- Erasing in order ---
643791 microseconds
AVL TREE, 4000000
--- Inserting in order ---
936047 microseconds
--- Searching all afterwards ---
632872 microseconds
--- Erasing in order ---
722934 microseconds
RED-BLACK TREE, 4000000
--- Inserting in order ---
1226726 microseconds
--- Searching all afterwards ---
667614 microseconds
--- Erasing in order ---
875269 microseconds
AVL TREE, 5000000
--- Inserting in order ---
1178134 microseconds
--- Searching all afterwards ---
823000 microseconds
--- Erasing in order ---
921291 microseconds
RED-BLACK TREE, 5000000
--- Inserting in order ---
1575743 microseconds
--- Searching all afterwards ---
861085 microseconds
--- Erasing in order ---
1171663 microseconds
AVL TREE, 6000000
--- Inserting in order ---
1441767 microseconds
--- Searching all afterwards ---
1003871 microseconds
--- Erasing in order ---
1143342 microseconds
RED-BLACK TREE, 6000000
--- Inserting in order ---
1918461 microseconds
--- Searching all afterwards ---
1055850 microseconds
--- Erasing in order ---
1437743 microseconds
AVL TREE, 7000000
--- Inserting in order ---
1710106 microseconds
--- Searching all afterwards ---
1205899 microseconds
--- Erasing in order ---
1391172 microseconds
RED-BLACK TREE, 7000000
--- Inserting in order ---
2292231 microseconds
--- Searching all afterwards ---
1235369 microseconds
--- Erasing in order ---
1623608 microseconds
AVL TREE, 8000000
--- Inserting in order ---
1978473 microseconds
--- Searching all afterwards ---
1379757 microseconds
--- Erasing in order ---
1592189 microseconds
RED-BLACK TREE, 8000000
--- Inserting in order ---
2691522 microseconds
--- Searching all afterwards ---
1446219 microseconds
--- Erasing in order ---
1907239 microseconds
AVL TREE, 9000000
--- Inserting in order ---
2284785 microseconds
--- Searching all afterwards ---
1588427 microseconds
--- Erasing in order ---
1818956 microseconds
RED-BLACK TREE, 9000000
--- Inserting in order ---
2987662 microseconds
--- Searching all afterwards ---
1648480 microseconds
--- Erasing in order ---
2160594 microseconds
TOTAL PROGRAM RUNNING TIME:
59642916 microseconds
--- Inserting in order ---
212706 microseconds
--- Searching all afterwards ---
118500 microseconds
--- Erasing in order ---
144221 microseconds
RED-BLACK TREE, 1000000
--- Inserting in order ---
253050 microseconds
--- Searching all afterwards ---
128795 microseconds
--- Erasing in order ---
177536 microseconds
AVL TREE, 2000000
--- Inserting in order ---
444879 microseconds
--- Searching all afterwards ---
297211 microseconds
--- Erasing in order ---
324151 microseconds
RED-BLACK TREE, 2000000
--- Inserting in order ---
578794 microseconds
--- Searching all afterwards ---
308073 microseconds
--- Erasing in order ---
394074 microseconds
AVL TREE, 3000000
--- Inserting in order ---
697510 microseconds
--- Searching all afterwards ---
466717 microseconds
--- Erasing in order ---
510471 microseconds
RED-BLACK TREE, 3000000
--- Inserting in order ---
901346 microseconds
--- Searching all afterwards ---
502883 microseconds
--- Erasing in order ---
643791 microseconds
AVL TREE, 4000000
--- Inserting in order ---
936047 microseconds
--- Searching all afterwards ---
632872 microseconds
--- Erasing in order ---
722934 microseconds
RED-BLACK TREE, 4000000
--- Inserting in order ---
1226726 microseconds
--- Searching all afterwards ---
667614 microseconds
--- Erasing in order ---
875269 microseconds
AVL TREE, 5000000
--- Inserting in order ---
1178134 microseconds
--- Searching all afterwards ---
823000 microseconds
--- Erasing in order ---
921291 microseconds
RED-BLACK TREE, 5000000
--- Inserting in order ---
1575743 microseconds
--- Searching all afterwards ---
861085 microseconds
--- Erasing in order ---
1171663 microseconds
AVL TREE, 6000000
--- Inserting in order ---
1441767 microseconds
--- Searching all afterwards ---
1003871 microseconds
--- Erasing in order ---
1143342 microseconds
RED-BLACK TREE, 6000000
--- Inserting in order ---
1918461 microseconds
--- Searching all afterwards ---
1055850 microseconds
--- Erasing in order ---
1437743 microseconds
AVL TREE, 7000000
--- Inserting in order ---
1710106 microseconds
--- Searching all afterwards ---
1205899 microseconds
--- Erasing in order ---
1391172 microseconds
RED-BLACK TREE, 7000000
--- Inserting in order ---
2292231 microseconds
--- Searching all afterwards ---
1235369 microseconds
--- Erasing in order ---
1623608 microseconds
AVL TREE, 8000000
--- Inserting in order ---
1978473 microseconds
--- Searching all afterwards ---
1379757 microseconds
--- Erasing in order ---
1592189 microseconds
RED-BLACK TREE, 8000000
--- Inserting in order ---
2691522 microseconds
--- Searching all afterwards ---
1446219 microseconds
--- Erasing in order ---
1907239 microseconds
AVL TREE, 9000000
--- Inserting in order ---
2284785 microseconds
--- Searching all afterwards ---
1588427 microseconds
--- Erasing in order ---
1818956 microseconds
RED-BLACK TREE, 9000000
--- Inserting in order ---
2987662 microseconds
--- Searching all afterwards ---
1648480 microseconds
--- Erasing in order ---
2160594 microseconds
TOTAL PROGRAM RUNNING TIME:
59642916 microseconds
The graph:
Analysis:
As you can see, the AVL tree even beats the red-black tree for ordered data in all cases (though only slightly for searching). Again, I reran this test for my old AVL tree and std::set, and I got similar results.
Conclusions:
Per all of the above data, the AVL tree beats the red-black tree in every area tested. I fear, however, that my results may be incorrect. First, no sources (including my hypothesis) have stated that AVL trees always beat red-black trees. Second, my compiler3 uses a red-black tree for its std::set implementation; why would it do that if AVL trees are superior?
While my tests have shown that AVL trees are better than red-black trees in inserting, deleting, and finding data either in order or randomly, I believe that more tests and studies need to be done to conclusively state one way or another where AVL trees are superior to red-black trees and vice-versa.
Footnotes and references:
1 - AVL tree. Wikipedia. Spring 2012. <http://en.wikipedia.org/wiki/AVL_tree>.
2 - My tutorial on red-black trees. <http://nathanbelue.blogspot.com/2012/04/red-black-trees.html>.
3 - g++ version 4.6.1
Update May 3, 2012
I spent the day running tests. I tested the trees above along with my personal AVL and red-black trees, std::set from g++ 4.6.1, and a third-party's tree. I tested inserting in ascending order, inserting in descending order, inserting using rand(), inserting using the random number generator above, and inserting using a random number generator that guarantees every number to be unique. I tested with data sets in the tens of thousands, hundreds of thousands, and millions. I inserted elements, deleted 90% of the elements, then inserted them back a few times. I tried different compiler optimizations. Heck, I even tried allocating memory for the trees beforehand so that memory allocation wouldn't be in the equation! Oh, and I made darn certain there was no noise, and I ran the tests several times.
In every single case, the AVL trees outperformed the red-black trees. The closest that the red-black trees ever came to beating the AVL trees was after inserting hundreds of thousands of unique random numbers (the results for this were too close to comfortably call the winner definitely). At least we know now that for up to five million nodes, the AVL tree is faster than the red-black tree.
I would like to thank #algorithms on freenode (especially wefawa) for taking the time (quite a bit of time) to consider this problem and help think up tests to run. There is one test that was not run because it was not figured out how to do it (try to maintain a Fibonacci tree), but I do not believe that it is likely to be found in any applications. The next step is to go beyond the 1 GB limit of my computer, and that requires some math. I will post again soon with the final verdict.
I did some tests with strings using AVL and Red-Black trees. AVL was faster and easier to write/optimize.
ReplyDeleteWhy AVL: faster to write, easy to debug, easy to improve, best ratio performance per lines of code.