Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_avlRk.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
49#ifndef TPL_AVLRK_H
50#define TPL_AVLRK_H
51
52#include <algorithm>
53#include <ahFunction.H>
54#include <tpl_arrayStack.H>
55#include <tpl_binNodeXt.H>
56#include <tpl_binTreeOps.H>
57#include <tpl_binNodeUtils.H>
58#include <avlNodeRk.H>
59#include <ah-errors.H>
60#include <ah-concepts.H>
61
62namespace Aleph {
106template <template <typename> class NodeType, typename Key, class Compare>
109{
110public:
112
113private:
118 Compare cmp;
119
124
126 {
127 if (avl_stack.is_empty())
128 {
130 return;
131 }
132
133 if (avl_stack.base() != head_ptr)
134 {
137 return;
138 }
139
140 if (const size_t to_pop = avl_stack.size() - 1; to_pop > 0)
141 avl_stack.popn(static_cast<int>(to_pop));
142 }
143
144 Node *search_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
145 {
147
148 Node *p = root;
149 Node *candidate = nullptr; // Tracks potential duplicate when going right
150 size_t candidate_pos = 0; // Stack size when candidate was set
151
152 do
153 {
156 avl_stack.push(p);
157 if (cmp(key, KEY(p))) // key < KEY(p)
158 {
160 p = LLINK(p);
161 }
162 else // key >= KEY(p), could be equal
163 {
164 candidate = p;
167 p = RLINK(p);
168 }
169 } while (p != Node::NullPtr);
170
171 // Optimistic duplicate check: when going right, key >= KEY(candidate)
172 // If also KEY(candidate) >= key (i.e., not less), then key == KEY(candidate)
173 if (candidate != nullptr and not cmp(KEY(candidate), key)) [[unlikely]]
174 {
176 // Truncate stack: remove nodes pushed after candidate
177 const size_t to_pop = avl_stack.size() > candidate_pos ? avl_stack.size() - candidate_pos : 0;
178 if (to_pop > 0)
179 avl_stack.popn(static_cast<int>(to_pop));
180 return candidate;
181 }
182
183 return avl_stack.top();
184 }
185
186 Node *search_dup_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
187 {
190
191 Node *p = root;
192 do
193 {
196 avl_stack.push(p);
197 if (cmp(key, KEY(p))) // key < KEY(p)?
198 {
200 p = LLINK(p);
201 }
202 else // key >= KEY(p)
203 {
205 p = RLINK(p);
206 }
207 } while (p != Node::NullPtr);
208
209 return avl_stack.top();
210 }
211
212 // Rotate to left with counter update
213 [[gnu::always_inline]] static Node *rotateLeft(Node *p) noexcept
214 {
215 assert(DIFF(p) == 2);
216 assert(RLINK(p) != Node::NullPtr);
217
218 Node *q = RLINK(p);
219 RLINK(p) = LLINK(q);
220 LLINK(q) = p;
221
222 // Update counters
223 COUNT(p) = COUNT(LLINK(p)) + COUNT(RLINK(p)) + 1;
224 COUNT(q) = COUNT(LLINK(q)) + COUNT(RLINK(q)) + 1;
225
226 if (DIFF(q) == 0)
227 {
228 DIFF(q) = -1;
229 DIFF(p) = 1;
230 }
231 else
232 DIFF(q) = DIFF(p) = 0;
233
234 return q;
235 }
236
237 // Rotate to right with counter update
238 [[gnu::always_inline]] static Node *rotateRight(Node *p) noexcept
239 {
240 assert(DIFF(p) == -2);
241 assert(LLINK(p) != Node::NullPtr);
242
243 Node *q = LLINK(p);
244 LLINK(p) = RLINK(q);
245 RLINK(q) = p;
246
247 // Update counters
248 COUNT(p) = COUNT(LLINK(p)) + COUNT(RLINK(p)) + 1;
249 COUNT(q) = COUNT(LLINK(q)) + COUNT(RLINK(q)) + 1;
250
251 if (DIFF(q) == 0)
252 {
253 DIFF(q) = 1;
254 DIFF(p) = -1;
255 }
256 else
257 DIFF(q) = DIFF(p) = 0;
258
259 return q;
260 }
261
262 // Double rotate left with counter update
263 [[gnu::always_inline]] static Node *doubleRotateLeft(Node *p) noexcept
264 {
265 assert(DIFF(p) == 2 or DIFF(p) == -2);
266 assert(RLINK(p) != Node::NullPtr and LLINK(RLINK(p)) != Node::NullPtr);
267
268 Node *q = RLINK(p);
269 Node *r = LLINK(q);
270 RLINK(p) = LLINK(r);
271 LLINK(q) = RLINK(r);
272 LLINK(r) = p;
273 RLINK(r) = q;
274
275 // Update counters
276 COUNT(p) = COUNT(LLINK(p)) + COUNT(RLINK(p)) + 1;
277 COUNT(q) = COUNT(LLINK(q)) + COUNT(RLINK(q)) + 1;
278 COUNT(r) = COUNT(LLINK(r)) + COUNT(RLINK(r)) + 1;
279
280 unsigned char b, c;
281 if (DIFF(r) == 1)
282 {
283 c = 1;
284 b = 0;
285 }
286 else if (DIFF(r) == -1)
287 {
288 c = 0;
289 b = 1;
290 }
291 else
292 c = b = 1;
293
294 DIFF(r) = 0;
295 DIFF(p) = b - 1;
296 DIFF(q) = 1 - c;
297
298 return r;
299 }
300
301 // Double rotate right with counter update
302 [[gnu::always_inline]]
303 static Node *doubleRotateRight(Node *p) noexcept
304 {
305 assert(DIFF(p) == 2 or DIFF(p) == -2);
306 assert(LLINK(p) != Node::NullPtr and RLINK(LLINK(p)) != Node::NullPtr);
307
308 Node *q = LLINK(p);
309 Node *r = RLINK(q);
310 LLINK(p) = RLINK(r);
311 RLINK(q) = LLINK(r);
312 RLINK(r) = p;
313 LLINK(r) = q;
314
315 // Update counters
316 COUNT(p) = COUNT(LLINK(p)) + COUNT(RLINK(p)) + 1;
317 COUNT(q) = COUNT(LLINK(q)) + COUNT(RLINK(q)) + 1;
318 COUNT(r) = COUNT(LLINK(r)) + COUNT(RLINK(r)) + 1;
319
320 unsigned char b, c;
321 if (DIFF(r) == 1)
322 {
323 c = 1;
324 b = 0;
325 }
326 else if (DIFF(r) == -1)
327 {
328 c = 0;
329 b = 1;
330 }
331 else
332 c = b = 1;
333
334 DIFF(r) = 0;
335 DIFF(p) = 1 - c;
336 DIFF(q) = b - 1;
337
338 return r;
339 }
340
348
349 static Rotation_Type rotation_type(Node *p) noexcept
350 {
351 assert(DIFF(p) == 2 or DIFF(p) == -2);
352
353 Node *pc;
354 if (DIFF(p) == 2)
355 {
356 pc = RLINK(p);
357 if (DIFF(pc) == 1 or DIFF(pc) == 0)
358 return ROTATE_LEFT;
359 return DOUBLE_ROTATE_LEFT;
360 }
361
362 pc = LLINK(p);
363 if (DIFF(pc) == -1 or DIFF(pc) == 0)
364 return ROTATE_RIGHT;
365
366 return DOUBLE_ROTATE_RIGHT;
367 }
368
369 static Node *restore_avl(Node *p, Node *pp) noexcept
370 {
371 assert(LLINK(pp) == p or RLINK(pp) == p);
372 assert(DIFF(p) == -2 or DIFF(p) == 2);
373
374 Node **link = LLINK(pp) == p ? &LLINK(pp) : &RLINK(pp);
375 switch (rotation_type(p))
376 {
377 case ROTATE_LEFT:
378 return *link = rotateLeft(p);
379 case ROTATE_RIGHT:
380 return *link = rotateRight(p);
382 return *link = doubleRotateLeft(p);
384 return *link = doubleRotateRight(p);
385 default:
386 AH_ERROR("Invalid rotation type");
387 break;
388 }
389
390 return nullptr;
391 }
392
394 {
395 Node *pp = avl_stack.pop();
396 if (LLINK(pp) == p)
397 --DIFF(pp);
398 else
399 ++DIFF(pp);
400
401 if (DIFF(pp) == 0)
402 {
404 return;
405 }
406
407 if (avl_stack_at_base())
408 return;
409
410 do
411 {
412 Node *gpp = avl_stack.pop();
413 if (LLINK(gpp) == pp)
414 --DIFF(gpp);
415 else
416 ++DIFF(gpp);
417
418 if (DIFF(gpp) == 0)
419 break;
420 if (DIFF(gpp) == -2 or DIFF(gpp) == 2)
421 {
422 Node *ggpp = avl_stack.pop();
424 break;
425 }
426
427 pp = gpp;
428 } while (not avl_stack_at_base());
429
431 }
432
434 {
436
437 Node *fSucc = p;
438 Node *succ = RLINK(p);
439
440 avl_stack.push(succ);
441
442 while (LLINK(succ) != Node::NullPtr)
443 {
444 fSucc = succ;
445 succ = LLINK(succ);
446 avl_stack.push(succ);
447 }
448
449 ref_to_stack_top = succ;
450 avl_stack.top() = p;
451
452 if (LLINK(pp) == p)
453 LLINK(pp) = succ;
454 else
455 RLINK(pp) = succ;
456
457 LLINK(succ) = LLINK(p);
458 LLINK(p) = Node::NullPtr;
459
460 if (RLINK(p) == succ)
461 {
462 RLINK(p) = RLINK(succ);
463 RLINK(succ) = p;
464 pp = succ;
465 }
466 else
467 {
468 Node *succr = RLINK(succ);
469 RLINK(succ) = RLINK(p);
470 LLINK(fSucc) = p;
471 RLINK(p) = succr;
472 pp = fSucc;
473 }
474
475 // Swap balance factors
476 DIFF(succ) = DIFF(p);
477
478 // Swap counters
479 COUNT(succ) = COUNT(p);
480
481 return succ;
482 }
483
485 {
486 Node *pp = avl_stack.top(1);
487 Node *ppp = avl_stack.popn(3);
488
489 while (true)
490 {
491 if (left_deficit)
492 ++DIFF(pp);
493 else
494 --DIFF(pp);
495
496 if (DIFF(pp) == -2 or DIFF(pp) == 2)
497 pp = restore_avl(pp, ppp);
498
499 if (DIFF(pp) != 0 or pp == root)
500 break;
501
502 left_deficit = LLINK(ppp) == pp;
503 pp = ppp;
504 ppp = avl_stack.pop();
505 }
506
508 }
509
510 // Update counters along the stack after insertion
512 {
513 // Stack contains: [head_ptr, ..., nodes in path, ..., closest to insertion]
514 // top(0) = closest node, top(size-1) = head_ptr (don't update)
515 const size_t sz = avl_stack.size();
516 for (size_t i = 0; i < sz - 1; ++i)
517 ++COUNT(avl_stack.top(i));
518 }
519
520 // Update counters along the stack after deletion
522 {
523 const size_t sz = avl_stack.size();
524 for (size_t i = 0; i < sz - 1; ++i)
525 --COUNT(avl_stack.top(i));
526 }
527
528public:
529 using key_type = Key;
530
532 [[nodiscard]] constexpr Compare &key_comp() noexcept
533 {
534 return cmp;
535 }
536
538 [[nodiscard]] constexpr Compare &get_compare() noexcept
539 {
540 return key_comp();
541 }
542
543 Gen_Avl_Tree_Rk(Compare cmf_fct = Compare()) noexcept
545 {
547 }
548
554 void swap(Gen_Avl_Tree_Rk &tree) noexcept
555 {
556 std::swap(root, tree.root);
557 std::swap(cmp, tree.cmp);
558 }
559
561 {
563 }
564
566 [[nodiscard]] constexpr Node *&getRoot() noexcept
567 {
568 return root;
569 }
570
573 {
574 return root;
575 }
576
579 {
580 return COUNT(root);
581 }
582
584 [[nodiscard]] constexpr bool is_empty() const noexcept
585 {
586 return root == Node::NullPtr;
587 }
588
591 [[nodiscard]] Node *search(const Key &key) const noexcept
592 {
594 return result == Node::NullPtr ? nullptr : result;
595 }
596
602 [[nodiscard]] Node *insert(Node *p) noexcept
603 {
604 if (root == Node::NullPtr)
605 return root = p;
606
607 signed char cmp_result = CmpEqual;
609 if (cmp_result == CmpLess)
610 LLINK(pp) = p;
611 else if (cmp_result == CmpGreater)
612 RLINK(pp) = p;
613 else
614 {
616 return nullptr;
617 }
618
621
622 return p;
623 }
624
633 {
634 if (root == Node::NullPtr)
635 return root = p;
636
637 signed char cmp_result = CmpEqual;
639 if (cmp_result == CmpLess)
640 LLINK(pp) = p;
641 else if (cmp_result == CmpGreater)
642 RLINK(pp) = p;
643 else
644 {
646 return pp;
647 }
648
651
652 return p;
653 }
654
656 [[nodiscard]] Node *insert_dup(Node *p) noexcept
657 {
658 if (root == Node::NullPtr)
659 return root = p;
660
661 signed char cmp_result = CmpEqual;
663 if (cmp_result == CmpLess)
664 LLINK(pp) = p;
665 else
666 RLINK(pp) = p;
667
670
671 return p;
672 }
673
676 [[nodiscard]] Node *remove(const Key &key) noexcept
677 {
678 if (root == Node::NullPtr)
679 return nullptr;
680
681 signed char cmp_result = CmpEqual;
683 if (cmp_result != CmpEqual)
684 {
686 return nullptr;
687 }
688
689 Node *pp = avl_stack.top(1);
690 bool left_deficit;
691
692 while (true)
693 {
694 left_deficit = LLINK(pp) == p;
695 if (LLINK(p) == Node::NullPtr)
696 {
697 if (LLINK(pp) == p)
698 LLINK(pp) = RLINK(p);
699 else
700 RLINK(pp) = RLINK(p);
701 break;
702 }
703
704 if (RLINK(p) == Node::NullPtr)
705 {
706 if (LLINK(pp) == p)
707 LLINK(pp) = LLINK(p);
708 else
709 RLINK(pp) = LLINK(p);
710 break;
711 }
712
714 }
715
716 p->reset();
717
718 if (pp == head_ptr)
719 {
721 return p;
722 }
723
726
727 return p;
728 }
729
737 Node *select(const size_t i) const
738 {
739 return Aleph::select(root, i);
740 }
741
748 std::pair<long, Node *> position(const Key &key) const noexcept
749 {
750 std::pair<long, Node *> ret_val;
751
752 ret_val.first =
753 BinTreeXt_Operation<Node, Compare>(cmp).inorder_position(root, key, ret_val.second);
754
755 return ret_val;
756 }
757
763 std::pair<long, Node *> find_position(const Key &key) const noexcept
764 {
765 std::pair<long, Node *> r(-2, nullptr);
766
767 r.first = BinTreeXt_Operation<Node, Compare>(cmp).find_position(root, key, r.second);
768
769 return r;
770 }
771
774 {
776 }
777
778private:
779 // ---------------------------------------------------------------------
780 // O(log n) join/split machinery: NOT yet wired to the public interface.
781 //
782 // The public join_exclusive(), split_key(), split_key_dup() and
783 // split_pos() below still use the simple O(n) dismantle-and-reinsert
784 // route. These routines are the intended replacement (join/split by
785 // spine descent with a pivot) and are the authoritative algorithm once
786 // connected; until then they are unreachable and unexercised, so treat
787 // the O(n) versions as the ones in force.
788 //
789 // TODO: wire these into the public join/split and drop the O(n) paths.
790 // Requires dedicated tests: nothing currently instantiates this code.
791 // ---------------------------------------------------------------------
792
793 // Compute height of AVL tree in O(log n) using balance factors
794 static size_t avl_height(Node *p) noexcept
795 {
796 size_t h = 0;
797 while (p != Node::NullPtr)
798 {
799 ++h;
800 // Follow the taller subtree
801 if (DIFF(p) >= 0)
802 p = RLINK(p); // right is taller or equal
803 else
804 p = LLINK(p); // left is taller
805 }
806 return h;
807 }
808
809 // Extract and remove the minimum node from tree rooted at p
810 // Returns the extracted node and updates p to the new root
811 static Node *extract_min(Node *&p) noexcept
812 {
813 if (p == Node::NullPtr)
814 return Node::NullPtr;
815
816 if (LLINK(p) == Node::NullPtr)
817 {
818 Node *ret = p;
819 p = RLINK(p);
820 LLINK(ret) = RLINK(ret) = Node::NullPtr;
821 COUNT(ret) = 1;
822 DIFF(ret) = 0;
823 return ret;
824 }
825
826 // Stack for rebalancing
827 FixedStack<Node **> stack(Node::MaxHeight);
828 Node **pp = &p;
829
830 while (LLINK(*pp) != Node::NullPtr)
831 {
832 stack.push(pp);
833 pp = &LLINK(*pp);
834 }
835
836 Node *ret = *pp;
837 *pp = RLINK(ret);
838
839 // Update counts and rebalance going up
840 while (not stack.is_empty())
841 {
842 Node **parent_ptr = stack.pop();
843 Node *parent = *parent_ptr;
844 --COUNT(parent);
845 ++DIFF(parent); // left subtree got shorter
846
847 if (DIFF(parent) == 2)
848 {
849 // Need to rebalance
850 Node *q = RLINK(parent);
851 if (DIFF(q) >= 0)
852 *parent_ptr = rotateLeft(parent);
853 else
854 *parent_ptr = doubleRotateLeft(parent);
855 }
856 else if (DIFF(parent) == 1)
857 break; // height didn't change
858 }
859
860 LLINK(ret) = RLINK(ret) = Node::NullPtr;
861 COUNT(ret) = 1;
862 DIFF(ret) = 0;
863 return ret;
864 }
865
866 // Extract and remove the maximum node from tree rooted at p
867 static Node *extract_max(Node *&p) noexcept
868 {
869 if (p == Node::NullPtr)
870 return Node::NullPtr;
871
872 if (RLINK(p) == Node::NullPtr)
873 {
874 Node *ret = p;
875 p = LLINK(p);
876 LLINK(ret) = RLINK(ret) = Node::NullPtr;
877 COUNT(ret) = 1;
878 DIFF(ret) = 0;
879 return ret;
880 }
881
882 FixedStack<Node **> stack(Node::MaxHeight);
883 Node **pp = &p;
884
885 while (RLINK(*pp) != Node::NullPtr)
886 {
887 stack.push(pp);
888 pp = &RLINK(*pp);
889 }
890
891 Node *ret = *pp;
892 *pp = LLINK(ret);
893
894 while (not stack.is_empty())
895 {
896 Node **parent_ptr = stack.pop();
897 Node *parent = *parent_ptr;
898 --COUNT(parent);
899 --DIFF(parent); // right subtree got shorter
900
901 if (DIFF(parent) == -2)
902 {
903 Node *q = LLINK(parent);
904 if (DIFF(q) <= 0)
905 *parent_ptr = rotateRight(parent);
906 else
907 *parent_ptr = doubleRotateRight(parent);
908 }
909 else if (DIFF(parent) == -1)
910 break;
911 }
912
913 LLINK(ret) = RLINK(ret) = Node::NullPtr;
914 COUNT(ret) = 1;
915 DIFF(ret) = 0;
916 return ret;
917 }
918
919 // Recursive join of two AVL trees where all keys in t1 < all keys in t2
920 // h1 and h2 are the heights of t1 and t2 respectively
921 static Node *join_exclusive_rec(Node *t1, size_t h1, Node *t2, size_t h2) noexcept
922 {
923 if (t1 == Node::NullPtr)
924 return t2;
925 if (t2 == Node::NullPtr)
926 return t1;
927
928 if (h1 >= h2)
929 {
930 // Extract max from t1 to use as pivot
932 if (t1 != Node::NullPtr)
933 h1 = avl_height(t1);
934 else
935 h1 = 0;
936
937 return join_with_pivot(t1, h1, pivot, t2, h2);
938 }
939 // Extract min from t2 to use as pivot
941 if (t2 != Node::NullPtr)
942 h2 = avl_height(t2);
943 else
944 h2 = 0;
945
946 return join_with_pivot(t1, h1, pivot, t2, h2);
947 }
948
949 // Join t1 and t2 using pivot as the connecting node
950 // All keys in t1 < pivot < all keys in t2
951 static Node *join_with_pivot(Node *t1, const size_t h1, Node *pivot, Node *t2,
952 const size_t h2) noexcept
953 {
954 if (h1 <= h2 + 1 and h2 <= h1 + 1)
955 {
956 // Heights are close enough, pivot becomes root
957 LLINK(pivot) = t1;
958 RLINK(pivot) = t2;
959 DIFF(pivot) = static_cast<signed char>(static_cast<long>(h2) - static_cast<long>(h1));
960 COUNT(pivot) = COUNT(t1) + COUNT(t2) + 1;
961 return pivot;
962 }
963
964 if (h1 > h2)
965 {
966 // t1 is taller, descend into right spine of t1
967 Node *result = join_right(t1, h1, pivot, t2, h2);
968 return result;
969 }
970 // t2 is taller, descend into left spine of t2
971 Node *result = join_left(t1, h1, pivot, t2, h2);
972 return result;
973 }
974
975 // Join when h1 > h2 + 1: descend right spine of t1
976 static Node *join_right(Node *t1, const size_t h1, Node *pivot, Node *t2, const size_t h2) noexcept
977 {
978 FixedStack<Node **> stack(Node::MaxHeight);
979 Node **pp = &t1;
980 size_t curr_h = h1;
981
982 // Descend right spine until we find a subtree of appropriate height
983 while (curr_h > h2 + 1 and *pp != Node::NullPtr)
984 {
985 stack.push(pp);
986 if (DIFF(*pp) >= 0)
987 curr_h--; // right child has height curr_h - 1
988 else
989 curr_h -= 2; // right child has height curr_h - 2 (since left is curr_h - 1)
990
991 pp = &RLINK(*pp);
992 }
993
994 // Now *pp points to a subtree of height <= h2 + 1
995 // Join pivot with *pp and t2
996 LLINK(pivot) = *pp;
997 RLINK(pivot) = t2;
998 const size_t left_h = (*pp != Node::NullPtr) ? avl_height(*pp) : 0;
999 DIFF(pivot) = static_cast<signed char>(static_cast<long>(h2) - static_cast<long>(left_h));
1000 COUNT(pivot) = COUNT(*pp) + COUNT(t2) + 1;
1001 *pp = pivot;
1002
1003 // Rebalance going up
1004 while (not stack.is_empty())
1005 {
1006 Node **parent_ptr = stack.pop();
1007 Node *parent = *parent_ptr;
1008 COUNT(parent) = COUNT(LLINK(parent)) + COUNT(RLINK(parent)) + 1;
1009
1010 const size_t new_right_h = avl_height(RLINK(parent));
1011 const size_t new_left_h = avl_height(LLINK(parent));
1012 DIFF(parent) = static_cast<signed char>(new_right_h) - static_cast<signed char>(new_left_h);
1013
1014 if (DIFF(parent) == 2)
1015 {
1016 if (Node *q = RLINK(parent); DIFF(q) >= 0)
1017 *parent_ptr = rotateLeft(parent);
1018 else
1019 *parent_ptr = doubleRotateLeft(parent);
1020 }
1021 }
1022
1023 return t1;
1024 }
1025
1026 // Join when h2 > h1 + 1: descend left spine of t2
1027 static Node *join_left(Node *t1, const size_t h1, Node *pivot, Node *t2, const size_t h2) noexcept
1028 {
1029 FixedStack<Node **> stack(Node::MaxHeight);
1030 Node **pp = &t2;
1031 size_t curr_h = h2;
1032
1033 while (curr_h > h1 + 1 and *pp != Node::NullPtr)
1034 {
1035 stack.push(pp);
1036 if (DIFF(*pp) <= 0)
1037 curr_h--;
1038 else
1039 curr_h -= 2;
1040
1041 pp = &LLINK(*pp);
1042 }
1043
1044 RLINK(pivot) = *pp;
1045 LLINK(pivot) = t1;
1046 const size_t right_h = (*pp != Node::NullPtr) ? avl_height(*pp) : 0;
1047 DIFF(pivot) = static_cast<signed char>(static_cast<long>(right_h) - static_cast<long>(h1));
1048 COUNT(pivot) = COUNT(t1) + COUNT(*pp) + 1;
1049 *pp = pivot;
1050
1051 while (not stack.is_empty())
1052 {
1053 Node **parent_ptr = stack.pop();
1054 Node *parent = *parent_ptr;
1055 COUNT(parent) = COUNT(LLINK(parent)) + COUNT(RLINK(parent)) + 1;
1056
1057 const size_t new_right_h = avl_height(RLINK(parent));
1058 const size_t new_left_h = avl_height(LLINK(parent));
1059 DIFF(parent) = static_cast<signed char>(new_right_h) - static_cast<signed char>(new_left_h);
1060
1061 if (DIFF(parent) == -2)
1062 {
1063 if (Node *q = LLINK(parent); DIFF(q) <= 0)
1064 *parent_ptr = rotateRight(parent);
1065 else
1066 *parent_ptr = doubleRotateRight(parent);
1067 }
1068 }
1069
1070 return t2;
1071 }
1072
1073 // Split by key recursively
1074 // Returns the node containing key (or NullPtr if not found)
1075 // t1 gets all keys < key, t2 gets all keys > key
1076 Node *split_key_rec(Node *p, const Key &key, Node *&t1, Node *&t2) noexcept
1077 {
1078 if (p == Node::NullPtr)
1079 {
1080 t1 = t2 = Node::NullPtr;
1081 return Node::NullPtr;
1082 }
1083
1084 Node *found;
1085 if (cmp(key, KEY(p)))
1086 {
1087 // key < p->key, go left
1088 Node *left_t2;
1089 found = split_key_rec(LLINK(p), key, t1, left_t2);
1090
1091 // p and its right subtree go to t2
1092 LLINK(p) = Node::NullPtr;
1093 const size_t left_t2_h = (left_t2 != Node::NullPtr) ? avl_height(left_t2) : 0;
1094 const size_t right_h = (RLINK(p) != Node::NullPtr) ? avl_height(RLINK(p)) : 0;
1095
1096 Node *right_tree = RLINK(p);
1097 RLINK(p) = Node::NullPtr;
1098 COUNT(p) = 1;
1099 DIFF(p) = 0;
1100
1102 }
1103 else if (cmp(KEY(p), key))
1104 {
1105 // key > p->key, go right
1106 Node *right_t1;
1107 found = split_key_rec(RLINK(p), key, right_t1, t2);
1108
1109 // p and its left subtree go to t1
1110 RLINK(p) = Node::NullPtr;
1111 const size_t right_t1_h = (right_t1 != Node::NullPtr) ? avl_height(right_t1) : 0;
1112 const size_t left_h = (LLINK(p) != Node::NullPtr) ? avl_height(LLINK(p)) : 0;
1113
1114 Node *left_tree = LLINK(p);
1115 LLINK(p) = Node::NullPtr;
1116 COUNT(p) = 1;
1117 DIFF(p) = 0;
1118
1120 }
1121 else
1122 {
1123 // Found the key
1124 t1 = LLINK(p);
1125 t2 = RLINK(p);
1126 LLINK(p) = RLINK(p) = Node::NullPtr;
1127 COUNT(p) = 1;
1128 DIFF(p) = 0;
1129 found = p;
1130 }
1131
1132 return found;
1133 }
1134
1135 // Split by key for duplicates: keys <= key go to t1, keys > key go to t2
1136 void split_key_dup_rec(Node *p, const Key &key, Node *&t1, Node *&t2) noexcept
1137 {
1138 if (p == Node::NullPtr)
1139 {
1140 t1 = t2 = Node::NullPtr;
1141 return;
1142 }
1143
1144 if (cmp(key, KEY(p)))
1145 {
1146 // key < p->key, p goes to t2
1147 Node *left_t2;
1148 split_key_dup_rec(LLINK(p), key, t1, left_t2);
1149
1150 LLINK(p) = Node::NullPtr;
1151 const size_t left_t2_h = (left_t2 != Node::NullPtr) ? avl_height(left_t2) : 0;
1152 const size_t right_h = (RLINK(p) != Node::NullPtr) ? avl_height(RLINK(p)) : 0;
1153
1154 Node *right_tree = RLINK(p);
1155 RLINK(p) = Node::NullPtr;
1156 COUNT(p) = 1;
1157 DIFF(p) = 0;
1158
1160 }
1161 else
1162 {
1163 // key >= p->key, p goes to t1
1164 Node *right_t1;
1166
1167 RLINK(p) = Node::NullPtr;
1168 const size_t right_t1_h = (right_t1 != Node::NullPtr) ? avl_height(right_t1) : 0;
1169 const size_t left_h = (LLINK(p) != Node::NullPtr) ? avl_height(LLINK(p)) : 0;
1170
1171 Node *left_tree = LLINK(p);
1172 LLINK(p) = Node::NullPtr;
1173 COUNT(p) = 1;
1174 DIFF(p) = 0;
1175
1177 }
1178 }
1179
1180 // Split by position recursively
1181 // Nodes with position < pos go to t1, nodes with position >= pos go to t2
1182 void split_pos_rec(Node *p, size_t pos, Node *&t1, Node *&t2) noexcept
1183 {
1184 if (p == Node::NullPtr)
1185 {
1186 t1 = t2 = Node::NullPtr;
1187 return;
1188 }
1189
1190 if (const size_t left_count = COUNT(LLINK(p)); pos <= left_count)
1191 {
1192 // Split point is in left subtree or at p
1193 Node *left_t2;
1194 split_pos_rec(LLINK(p), pos, t1, left_t2);
1195
1196 // p goes to t2
1197 LLINK(p) = Node::NullPtr;
1198 const size_t left_t2_h = (left_t2 != Node::NullPtr) ? avl_height(left_t2) : 0;
1199 const size_t right_h = (RLINK(p) != Node::NullPtr) ? avl_height(RLINK(p)) : 0;
1200
1201 Node *right_tree = RLINK(p);
1202 RLINK(p) = Node::NullPtr;
1203 COUNT(p) = 1;
1204 DIFF(p) = 0;
1205
1207 }
1208 else
1209 {
1210 // Split point is in right subtree
1211 Node *right_t1;
1212 split_pos_rec(RLINK(p), pos - left_count - 1, right_t1, t2);
1213
1214 // p goes to t1
1215 RLINK(p) = Node::NullPtr;
1216 const size_t right_t1_h = (right_t1 != Node::NullPtr) ? avl_height(right_t1) : 0;
1217 const size_t left_h = (LLINK(p) != Node::NullPtr) ? avl_height(LLINK(p)) : 0;
1218
1219 Node *left_tree = LLINK(p);
1220 LLINK(p) = Node::NullPtr;
1221 COUNT(p) = 1;
1222 DIFF(p) = 0;
1223
1225 }
1226 }
1227
1228public:
1238 {
1239 if (t.root == Node::NullPtr)
1240 return;
1241
1242 if (root == Node::NullPtr)
1243 {
1244 root = t.root;
1245 t.root = Node::NullPtr;
1246 return;
1247 }
1248
1249 // Collect nodes from t in preorder and insert them one by one
1250 // This is O(m log(n+m)) but guarantees correctness
1251 FixedStack<Node *> stack(Node::MaxHeight);
1252 stack.push(t.root);
1253 t.root = Node::NullPtr;
1254
1255 while (not stack.is_empty())
1256 {
1257 Node *p = stack.pop();
1258 Node *left = LLINK(p);
1259 Node *right = RLINK(p);
1260
1261 // Reset node and insert
1262 LLINK(p) = RLINK(p) = Node::NullPtr;
1263 COUNT(p) = 1;
1264 DIFF(p) = 0;
1265 (void) insert(p);
1266
1267 if (right != Node::NullPtr)
1268 stack.push(right);
1269 if (left != Node::NullPtr)
1270 stack.push(left);
1271 }
1272 }
1273
1289 Node *split_key(const Key &key, Gen_Avl_Tree_Rk &t1, Gen_Avl_Tree_Rk &t2) noexcept
1290 {
1291 if (root == Node::NullPtr)
1292 return nullptr;
1293
1294 // Simple O(n) implementation: iterate through tree and partition
1295 Node *found = nullptr;
1296 FixedStack<Node *> stack(Node::MaxHeight);
1297 stack.push(root);
1298 root = Node::NullPtr;
1299
1300 while (not stack.is_empty())
1301 {
1302 Node *p = stack.pop();
1303 Node *left = LLINK(p);
1304 Node *right = RLINK(p);
1305
1306 if (right != Node::NullPtr)
1307 stack.push(right);
1308 if (left != Node::NullPtr)
1309 stack.push(left);
1310
1311 LLINK(p) = RLINK(p) = Node::NullPtr;
1312 COUNT(p) = 1;
1313 DIFF(p) = 0;
1314
1315 if (cmp(KEY(p), key))
1316 (void) t1.insert(p);
1317 else if (cmp(key, KEY(p)))
1318 (void) t2.insert(p);
1319 else
1320 found = p; // Found the key
1321 }
1322
1323 return found;
1324 }
1325
1340 void split_key_dup(const Key &key, Gen_Avl_Tree_Rk &t1, Gen_Avl_Tree_Rk &t2) noexcept
1341 {
1342 if (root == Node::NullPtr)
1343 return;
1344
1345 FixedStack<Node *> stack(Node::MaxHeight);
1346 stack.push(root);
1347 root = Node::NullPtr;
1348
1349 while (not stack.is_empty())
1350 {
1351 Node *p = stack.pop();
1352 Node *left = LLINK(p);
1353 Node *right = RLINK(p);
1354
1355 if (right != Node::NullPtr)
1356 stack.push(right);
1357 if (left != Node::NullPtr)
1358 stack.push(left);
1359
1360 LLINK(p) = RLINK(p) = Node::NullPtr;
1361 COUNT(p) = 1;
1362 DIFF(p) = 0;
1363
1364 if (cmp(key, KEY(p)))
1365 (void) t2.insert(p);
1366 else
1367 (void) t1.insert_dup(p);
1368 }
1369 }
1370
1385 void split_pos(const size_t pos, Gen_Avl_Tree_Rk &t1, Gen_Avl_Tree_Rk &t2) noexcept
1386 {
1387 if (root == Node::NullPtr)
1388 return;
1389
1390 if (pos == 0)
1391 {
1392 t2.root = root;
1393 root = Node::NullPtr;
1394 return;
1395 }
1396
1397 if (pos >= size())
1398 {
1399 t1.root = root;
1400 root = Node::NullPtr;
1401 return;
1402 }
1403
1404 // Simple O(n) implementation using inorder traversal
1405 size_t count = 0;
1406 FixedStack<Node *> stack(Node::MaxHeight);
1407 Node *curr = root;
1408 root = Node::NullPtr;
1409
1410 // Inorder traversal
1411 while (curr != Node::NullPtr or not stack.is_empty())
1412 {
1413 while (curr != Node::NullPtr)
1414 {
1415 stack.push(curr);
1416 Node *left = LLINK(curr);
1417 LLINK(curr) = Node::NullPtr; // Disconnect
1418 curr = left;
1419 }
1420
1421 curr = stack.pop();
1422 Node *right = RLINK(curr);
1423 RLINK(curr) = Node::NullPtr;
1424 COUNT(curr) = 1;
1425 DIFF(curr) = 0;
1426
1427 if (count < pos)
1428 (void) t1.insert(curr);
1429 else
1430 (void) t2.insert(curr);
1431
1432 ++count;
1433 curr = right;
1434 }
1435 }
1436
1445 class Iterator : public BinTreeXt_Iterator<Gen_Avl_Tree_Rk, Node, Key, Compare>
1446 {
1448
1449 public:
1450 using Base::Base;
1451 using Base::operator =;
1452 }; // end class Iterator
1453};
1454
1466template <typename Key, class Compare = Aleph::less<Key>>
1468struct Avl_Tree_Rk : public Gen_Avl_Tree_Rk<AvlNodeRk, Key, Compare>
1469{
1471 using Base::Base;
1472};
1473
1485template <typename Key, class Compare = Aleph::less<Key>>
1487struct Avl_Tree_Rk_Vtl : public Gen_Avl_Tree_Rk<AvlNodeRkVtl, Key, Compare>
1488{
1490 using Base::Base;
1491};
1492} // end namespace Aleph
1493
1494#endif // TPL_AVLRK_H
C++20 concepts hub: comparison, BST policy, and Aleph container concepts.
Exception handling system with formatted messages for Aleph-w.
#define AH_ERROR(...)
Print an error message (always enabled).
Definition ahDefs.H:270
Standard functor implementations and comparison objects.
AVL tree node with rank (subtree count).
#define DIFF(p)
Access the balance factor of node p.
Definition avlNode.H:95
@ KEY
Definition btreepic.C:169
long double h
Definition btreepic.C:154
Base iterator template for ranked binary search trees.
Fixed length stack.
T & base() noexcept
Return the internal array base.
size_t size() const noexcept
Return the number of elements stored in the stack.
T popn(const int &n) noexcept
Perform in constant time n pops.
bool is_empty() const noexcept
Return true if stack is empty.
T pop() noexcept
Pop by moving the top of stack.
T & top() noexcept
Return a modifiable reference to stack's top.
void empty() noexcept
Empty the stack.
T & push(const T &data) noexcept(std::is_nothrow_copy_assignable_v< T >)
Push a copy of data
Iterator over the nodes.
Definition tpl_avlRk.H:1446
AVL balanced binary search tree with rank (order statistics).
Definition tpl_avlRk.H:109
Node * insert(Node *p) noexcept
Insert the node pointed by p in the tree.
Definition tpl_avlRk.H:602
void split_key_dup_rec(Node *p, const Key &key, Node *&t1, Node *&t2) noexcept
Definition tpl_avlRk.H:1136
static Node * join_with_pivot(Node *t1, const size_t h1, Node *pivot, Node *t2, const size_t h2) noexcept
Definition tpl_avlRk.H:951
constexpr Compare & key_comp() noexcept
The key type.
Definition tpl_avlRk.H:532
NodeType< Key > Node
Definition tpl_avlRk.H:111
Node * search_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avlRk.H:144
static size_t avl_height(Node *p) noexcept
Definition tpl_avlRk.H:794
constexpr Node *& getRoot() noexcept
Return a modifiable reference to tree's root.
Definition tpl_avlRk.H:566
bool avl_stack_at_base() noexcept
Definition tpl_avlRk.H:120
static Node * join_exclusive_rec(Node *t1, size_t h1, Node *t2, size_t h2) noexcept
Definition tpl_avlRk.H:921
Gen_Avl_Tree_Rk(Compare cmf_fct=Compare()) noexcept
Definition tpl_avlRk.H:543
Node * select(const size_t i) const
Return the i-th node in order sense.
Definition tpl_avlRk.H:737
Node * search_dup_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avlRk.H:186
void update_counters_after_insertion() noexcept
Definition tpl_avlRk.H:511
static Node * join_left(Node *t1, const size_t h1, Node *pivot, Node *t2, const size_t h2) noexcept
Definition tpl_avlRk.H:1027
Node * search_or_insert(Node *p) noexcept
Search or insert a key.
Definition tpl_avlRk.H:632
static Rotation_Type rotation_type(Node *p) noexcept
Definition tpl_avlRk.H:349
void restore_avl_after_insertion(Node *p) noexcept
Definition tpl_avlRk.H:393
std::pair< long, Node * > find_position(const Key &key) const noexcept
Find the inorder position of a key in the tree.
Definition tpl_avlRk.H:763
Node * split_key_rec(Node *p, const Key &key, Node *&t1, Node *&t2) noexcept
Definition tpl_avlRk.H:1076
Node * remove(const Key &key) noexcept
Remove from tree the node containing key.
Definition tpl_avlRk.H:676
static Node * restore_avl(Node *p, Node *pp) noexcept
Definition tpl_avlRk.H:369
static Node * rotateLeft(Node *p) noexcept
Definition tpl_avlRk.H:213
size_t size() const noexcept
Return the number of nodes in the tree.
Definition tpl_avlRk.H:578
void split_pos_rec(Node *p, size_t pos, Node *&t1, Node *&t2) noexcept
Definition tpl_avlRk.H:1182
static Node * extract_max(Node *&p) noexcept
Definition tpl_avlRk.H:867
virtual ~Gen_Avl_Tree_Rk() noexcept
Definition tpl_avlRk.H:560
static Node * extract_min(Node *&p) noexcept
Definition tpl_avlRk.H:811
static Node * rotateRight(Node *p) noexcept
Definition tpl_avlRk.H:238
void join_exclusive(Gen_Avl_Tree_Rk &t) noexcept
Join this tree exclusively with another tree.
Definition tpl_avlRk.H:1237
Node * swapWithSuccessor(Node *p, Node *&pp) noexcept
Definition tpl_avlRk.H:433
constexpr bool is_empty() const noexcept
Return true if tree is empty.
Definition tpl_avlRk.H:584
static Node * doubleRotateLeft(Node *p) noexcept
Definition tpl_avlRk.H:263
constexpr Compare & get_compare() noexcept
Definition tpl_avlRk.H:538
void swap(Gen_Avl_Tree_Rk &tree) noexcept
Swap in constant time all the items of this with the items of tree.
Definition tpl_avlRk.H:554
Node * insert_dup(Node *p) noexcept
Insert the node p without testing for key duplicity.
Definition tpl_avlRk.H:656
void restore_avl_after_deletion(bool left_deficit) noexcept
Definition tpl_avlRk.H:484
void split_key_dup(const Key &key, Gen_Avl_Tree_Rk &t1, Gen_Avl_Tree_Rk &t2) noexcept
Split tree by key including duplicates.
Definition tpl_avlRk.H:1340
static Node * doubleRotateRight(Node *p) noexcept
Definition tpl_avlRk.H:303
Node * split_key(const Key &key, Gen_Avl_Tree_Rk &t1, Gen_Avl_Tree_Rk &t2) noexcept
Split tree by key.
Definition tpl_avlRk.H:1289
void split_pos(const size_t pos, Gen_Avl_Tree_Rk &t1, Gen_Avl_Tree_Rk &t2) noexcept
Split tree by inorder position.
Definition tpl_avlRk.H:1385
constexpr Node * getRoot() const noexcept
Return a pointer to the tree's root.
Definition tpl_avlRk.H:572
bool verify() const noexcept
Return true if the tree is a valid AVL tree with correct counters.
Definition tpl_avlRk.H:773
Node * search(const Key &key) const noexcept
Search a node containing key; if found, then a pointer to the node containing it is returned; otherwi...
Definition tpl_avlRk.H:591
void update_counters_after_deletion() noexcept
Definition tpl_avlRk.H:521
void clean_avl_stack() noexcept
Definition tpl_avlRk.H:125
FixedStack< Node * > avl_stack
The type of node.
Definition tpl_avlRk.H:114
std::pair< long, Node * > position(const Key &key) const noexcept
Compute the inorder position of a key.
Definition tpl_avlRk.H:748
static Node * join_right(Node *t1, const size_t h1, Node *pivot, Node *t2, const size_t h2) noexcept
Definition tpl_avlRk.H:976
Strict weak ordering constraint for BST comparators.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
bool check_rank_tree(Node *root) noexcept
Return true if root is a valid extended binary tree.
auto & COUNT(Node *p) noexcept
Return the number of nodes of the tree fron p is root.
Node * select(Node *r, const size_t pos)
Iterative selection of a node according to inorder position.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool is_avl_rk(Node *p)
Verify if tree rooted at p is a valid AVL tree with correct counters.
Definition avlNodeRk.H:89
and
Check uniqueness with explicit hash + equality functors.
@ CmpGreater
First argument is greater than second.
@ CmpLess
First argument is less than second.
@ CmpEqual
Arguments are equal.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
Ranked AVL tree with nodes with a virtual destructor.
Definition tpl_avlRk.H:1488
Ranked AVL tree with nodes without a virtual destructor.
Definition tpl_avlRk.H:1469
#define RLINK(i, n)
#define LLINK(i, n)
gsl_rng * r
Stack implementations backed by dynamic or fixed arrays.
Utility functions for binary tree operations.
Extended binary node with subtree count.
Binary tree operations (split, join, rotate).