Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_avl.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
32
51# ifndef TPL_AVL_H
52# define TPL_AVL_H
53
54# include <algorithm>
55# include <ahFunction.H>
56# include <tpl_arrayStack.H>
57# include <avlNode.H>
58# include <tpl_binNodeUtils.H>
59# include <ah-concepts.H>
60
61using namespace Aleph;
62
63namespace Aleph
64{
106 template <template <typename> class NodeType, typename Key, class Compare>
109 {
110 public:
112
113 private:
118 Compare cmp;
119
121 {
122 return not avl_stack.is_empty()
123 and avl_stack.size() == 1
125 }
126
128 {
129 if (avl_stack.is_empty())
130 {
132 return;
133 }
134
135 if (avl_stack.base() != head_ptr)
136 {
139 return;
140 }
141
142 if (const size_t to_pop = avl_stack.size() - 1; to_pop > 0)
143 avl_stack.popn(static_cast<int>(to_pop));
144 }
145
146 Node * search_and_stack_avl(const Key & key,
147 signed char & cmp_result) noexcept
148 {
150
151 Node *p = root;
152 Node *candidate = nullptr; // Tracks potential duplicate when going right
153 size_t candidate_pos = 0; // Stack size when candidate was set
154
155 do // descend searching for key and push the search path
156 {
159 avl_stack.push(p);
160 if (cmp(key, KEY(p))) // key < KEY(p)
161 {
163 p = LLINK(p);
164 }
165 else // key >= KEY(p), could be equal
166 {
167 candidate = p;
170 p = RLINK(p);
171 }
172 }
173 while (p != Node::NullPtr);
174
175 // Optimistic duplicate check: when going right, key >= KEY(candidate)
176 // If also KEY(candidate) >= key (i.e., not less), then key == KEY(candidate)
177 if (candidate != nullptr and not cmp(KEY(candidate), key)) [[unlikely]]
178 {
180 // Truncate stack: remove nodes pushed after candidate
181 const size_t to_pop =
184 if (to_pop > 0)
185 avl_stack.popn(static_cast<int>(to_pop));
186 return candidate;
187 }
188
189 return avl_stack.top();
190 }
191
193 signed char & cmp_result) noexcept
194 {
197
198 Node *p = root;
199 do // descend searching for key and push the search path
200 {
203 avl_stack.push(p);
204 if (cmp(key, KEY(p))) // key < KEY(p)?
205 {
207 p = LLINK(p);
208 }
209 else // key >= KEY(p)
210 {
212 p = RLINK(p);
213 }
214 }
215 while (p != Node::NullPtr);
216
217 return avl_stack.top();
218 }
219
220 [[gnu::always_inline]] static Node * rotateLeft(Node *p) noexcept
221 {
222 assert(DIFF(p) == 2);
223 assert(RLINK(p) != Node::NullPtr);
224
225 Node *q = RLINK(p);
226 RLINK(p) = LLINK(q);
227 LLINK(q) = p;
228
229 if (DIFF(q) == 0) // balance factors adjustment
230 { // this case happens during deletion
231 DIFF(q) = -1;
232 DIFF(p) = 1;
233 }
234 else
235 DIFF(q) = DIFF(p) = 0;
236
237 return q;
238 }
239
240 [[gnu::always_inline]] static Node * rotateRight(Node *p) noexcept
241 {
242 assert(DIFF(p) == -2);
243 assert(LLINK(p) != Node::NullPtr);
244
245 Node *q = LLINK(p);
246 LLINK(p) = RLINK(q);
247 RLINK(q) = p;
248
249 if (DIFF(q) == 0) // balance factors adjustment
250 { // this case happens during deletion
251 DIFF(q) = 1;
252 DIFF(p) = -1;
253 }
254 else
255 DIFF(q) = DIFF(p) = 0;
256
257 return q;
258 }
259
260 [[gnu::always_inline]] static Node * doubleRotateLeft(Node *p) noexcept
261 {
262 assert(DIFF(p) == 2 or DIFF(p) == -2);
263 assert(RLINK(p) != Node::NullPtr and LLINK(RLINK(p)) != Node::NullPtr);
264
265 Node *q = RLINK(p);
266 Node *r = LLINK(q);
267 RLINK(p) = LLINK(r);
268 LLINK(q) = RLINK(r);
269 LLINK(r) = p;
270 RLINK(r) = q;
271
272 unsigned char b; // logical height of r's left child
273 unsigned char c; // logical height of r's right child
274
275 // Determine logical heights of p, q and r
276 if (DIFF(r) == 1) // ==> c > b ==> c-b == 1
277 {
278 c = 1;
279 b = 0;
280 }
281 else if (DIFF(r) == -1) // ==> c < b ==> c-b = -1
282 {
283 c = 0;
284 b = 1;
285 }
286 else
287 c = b = 1;
288
289 // balance factors adjustment
290 DIFF(r) = 0;
291 DIFF(p) = b - 1; // logical height of p's left child is 1
292 DIFF(q) = 1 - c; // logical height of q's right child is 1
293
294 return r;
295 }
296
297 [[gnu::always_inline]] static Node * doubleRotateRight(Node *p) noexcept
298 {
299 assert(DIFF(p) == 2 or DIFF(p) == -2);
300 assert(LLINK(p) != Node::NullPtr and RLINK(LLINK(p)) != Node::NullPtr);
301
302 Node *q = LLINK(p);
303 Node *r = RLINK(q);
304 LLINK(p) = RLINK(r);
305 RLINK(q) = LLINK(r);
306 RLINK(r) = p;
307 LLINK(r) = q;
308
309 unsigned char b; // logical height of r's left child
310 unsigned char c; // logical height of r's right child
311
312 // determine logical heights of children of p, q and r
313 if (DIFF(r) == 1) // ==> c > b ==> c-b == 1
314 {
315 c = 1;
316 b = 0;
317 }
318 else if (DIFF(r) == -1) // ==> c < b ==> c-b == -1
319 {
320 c = 0;
321 b = 1;
322 }
323 else
324 c = b = 1;
325
326 // balance factors adjustment
327 DIFF(r) = 0;
328 DIFF(p) = 1 - c; // logical height of p's right child is 1
329 DIFF(q) = b - 1; // logical height of p's left child is 1
330
331 return r;
332 }
333
338
339 static Rotation_Type rotation_type(Node *p) noexcept
340 {
341 assert(DIFF(p) == 2 or DIFF(p) == -2);
342
343 Node *pc; // saves p's child
344 if (DIFF(p) == 2) // to the left
345 {
346 pc = RLINK(p);
347 if (DIFF(pc) == 1 or DIFF(pc) == 0)
348 return ROTATE_LEFT;
349
350 return DOUBLE_ROTATE_LEFT;
351 }
352
353 pc = LLINK(p);
354 if (DIFF(pc) == -1 or DIFF(pc) == 0)
355 return ROTATE_RIGHT;
356
357 return DOUBLE_ROTATE_RIGHT;
358 }
359
360 static Node * restore_avl(Node *p, Node *pp) noexcept
361 {
362 assert(LLINK(pp) == p or RLINK(pp) == p);
363 assert(DIFF(p) == -2 or DIFF(p) == 2);
364
365 Node **link = LLINK(pp) == p ? &LLINK(pp) : &RLINK(pp);
366 switch (rotation_type(p))
367 {
368 case ROTATE_LEFT: return *link = rotateLeft(p);
369 case ROTATE_RIGHT: return *link = rotateRight(p);
370 case DOUBLE_ROTATE_LEFT: return *link = doubleRotateLeft(p);
371 case DOUBLE_ROTATE_RIGHT: return *link = doubleRotateRight(p);
372
373 default:
374 AH_ERROR("Invalid rotation type");
375 break;
376 }
377
378 return nullptr;
379 }
380
382 {
383 Node *pp = avl_stack.pop(); // parent of the inserted node
384 if (LLINK(pp) == p) // adjust parent's balance factor
385 --DIFF(pp);
386 else
387 ++DIFF(pp);
388
389 if (DIFF(pp) == 0)
390 { // in this case, the height of pp's ancestor does not increase
392 return;
393 }
394
395 if (avl_stack_at_base())
396 return; // pp is the root
397 do // search a node whose balance factor becomes 0
398 {
399 Node *gpp = avl_stack.pop(); // parent of pp
400 // update balance factors
401 if (LLINK(gpp) == pp) // AH_ERROR if (Compare () (key, KEY(gpp)))
402 --DIFF(gpp);
403 else
404 ++DIFF(gpp);
405
406 if (DIFF(gpp) == 0)
407 break; // no rebalancing is needed
408 if (DIFF(gpp) == -2 or DIFF(gpp) == 2) // AVL violation?
409 { // yes ==> rebalancing is required
410 Node *ggpp = avl_stack.pop();
412 break;
413 }
414
415 pp = gpp; // AH_ERROR; add
416 }
417 while (not avl_stack_at_base());
418
420 }
421
422 Node * swapWithSuccessor(Node *p, Node *& pp) noexcept
423 { // Reference to the stack top, since p will be swapped with its
424 // successor and the successor will take p's position in the stack
426
427 Node *fSucc = p; // successor's parent
428 Node *succ = RLINK(p); // search starts from RLINK(p)
429
430 avl_stack.push(succ);
431
432 // find the successor while updating the stack
433 while (LLINK(succ) != Node::NullPtr) // descend as far left as possible
434 {
435 fSucc = succ;
436 succ = LLINK(succ);
437 avl_stack.push(succ);
438 }
439
440 // update old stack entry occupied by p: it is equivalent to swapping
441 // the old top (before searching succ) with the current one
442 ref_to_stack_top = succ;
443 avl_stack.top() = p;
444 if (LLINK(pp) == p) // update pp's new child (successor)
445 LLINK(pp) = succ;
446 else
447 RLINK(pp) = succ;
448
449 LLINK(succ) = LLINK(p); // swap left subtrees
450 LLINK(p) = Node::NullPtr;
451 if (RLINK(p) == succ) // update right subtrees
452 { // successor is exactly p's right child
453 RLINK(p) = RLINK(succ);
454 RLINK(succ) = p;
455 pp = succ;
456 }
457 else
458 { // successor is the leftmost descendant of RLINK(p)
459 Node *succr = RLINK(succ);
460 RLINK(succ) = RLINK(p);
461 LLINK(fSucc) = p;
462 RLINK(p) = succr;
463 pp = fSucc;
464 }
465
466 DIFF(succ) = DIFF(p); // swap balance factors
467
468 return succ;
469 }
470
472 {
473 Node *pp = avl_stack.top(1); // parent of p
474 Node *ppp = avl_stack.popn(3); // remove from stack p, parent and grandparent
475 while (true)
476 { // update balance factors
477 if (left_deficit) // AH_ERROR Compare () (key, KEY(pp)))
478 ++DIFF(pp);
479 else
480 --DIFF(pp);
481
482 if (DIFF(pp) == -2 or DIFF(pp) == 2) // still valid?
483 pp = restore_avl(pp, ppp); // no
484
485 if (DIFF(pp) != 0 or pp == root)
486 break; // global tree height has not changed ==> stop
487
488 left_deficit = LLINK(ppp) == pp;
489 pp = ppp; // advance to next ancestor
490 ppp = avl_stack.pop();
491 }
492
494 }
495
496 public:
497 using key_type = Key;
498
500 [[nodiscard]] constexpr Compare &key_comp() noexcept { return cmp; }
501
503 [[nodiscard]] constexpr Compare &get_compare() noexcept { return key_comp(); }
504
505 Gen_Avl_Tree(Compare _cmp = Compare()) noexcept
506 : avl_stack(Node::MaxHeight), head_ptr(&head_node),
508 {
510 }
511
517 void swap(Gen_Avl_Tree & tree) noexcept
518 {
519 std::swap(root, tree.root);
520 std::swap(cmp, tree.cmp);
521 }
522
524
526 [[nodiscard]] constexpr Node *&getRoot() noexcept { return root; }
527
529 [[nodiscard]] constexpr Node * getRoot() const noexcept { return root; }
530
533 [[nodiscard]] Node * search(const Key & key) const noexcept
534 {
535 Node *p = root;
536 while (p != Node::NullPtr)
537 {
540 if (cmp(key, KEY(p)))
541 p = LLINK(p);
542 else if (cmp(KEY(p), key))
543 p = RLINK(p);
544 else
545 return p;
546 }
547 return nullptr;
548 }
549
561 [[nodiscard]] Node * insert(Node *p) noexcept
562 {
563 if (root == Node::NullPtr)
564 return root = p;
565
566 signed char cmp_result = CmpEqual;
568 if (cmp_result == CmpLess)
569 LLINK(pp) = p;
570 else if (cmp_result == CmpGreater)
571 RLINK(pp) = p;
572 else
573 { // duplicated key
575 return nullptr;
576 }
577
579
580 return p;
581 }
582
599 {
600 if (root == Node::NullPtr)
601 return root = p;
602
603 signed char cmp_result = CmpEqual;
605 if (cmp_result == CmpLess)
606 LLINK(pp) = p;
607 else if (cmp_result == CmpGreater)
608 RLINK(pp) = p;
609 else
610 { // duplicated key
612 return pp;
613 }
614
616
617 return p;
618 }
619
621 [[nodiscard]] Node * insert_dup(Node *p) noexcept
622 {
623 if (root == Node::NullPtr)
624 return root = p;
625
626 signed char cmp_result = CmpEqual;
628 if (cmp_result == CmpLess)
629 LLINK(pp) = p;
630 else
631 RLINK(pp) = p;
632
634
635 return p;
636 }
637
640 [[nodiscard]] Node * remove(const Key & key) noexcept
641 {
642 if (root == Node::NullPtr)
643 return nullptr;
644
645 signed char cmp_result = CmpEqual;
647 if (cmp_result != CmpEqual)
648 { // key was not found
650 return nullptr;
651 }
652
653 Node *pp = avl_stack.top(1); // get parent of p
654 bool left_deficit; // AH_ERROR Key removed_key = KEY(p);
655 while (true)
656 {
657 left_deficit = LLINK(pp) == p;
658 if (LLINK(p) == Node::NullPtr) // missing left child?
659 { // yes: link pp to p's child
660 if (LLINK(pp) == p)
661 LLINK(pp) = RLINK(p);
662 else
663 RLINK(pp) = RLINK(p);
664 break;
665 }
666
667 if (RLINK(p) == Node::NullPtr) // missing right child?
668 { // yes: link pp to p's child
669 if (LLINK(pp) == p)
670 LLINK(pp) = LLINK(p);
671 else
672 RLINK(pp) = LLINK(p);
673 break;
674 }
675
676 // here p is a full node ==> swap with successor
678 // removed_key = KEY(succ); // AH_ERROR remove
679 }
680
681 p->reset();
682
683 if (pp == head_ptr) // check if the root was removed
684 { // balance factors unchanged ==> AVL condition is not violated
686 return p;
687 }
688
690
691 return p;
692 }
693
695 {
696 return is_avl(root);
697 }
698
717 };
718
719
734 template <typename Key, class Compare = Aleph::less<Key>>
736 struct Avl_Tree : public Gen_Avl_Tree<AvlNode, Key, Compare>
737 {
739 using Base::Base;
740 };
741
742
757 template <typename Key, class Compare = Aleph::less<Key>>
759 struct Avl_Tree_Vtl : public Gen_Avl_Tree<AvlNodeVtl, Key, Compare>
760 {
762 using Base::Base;
763 };
764} // end namespace Aleph
765# endif // TPL_AVL_H
C++20 concepts for constraining comparison functors.
#define AH_ERROR(format, args...)
Print an error message (always enabled).
Definition ahDefs.H:271
Standard functor implementations and comparison objects.
AVL tree node with balance factor.
bool is_avl(Node *p)
Validate that a tree satisfies AVL properties.
Definition avlNode.H:123
#define DIFF(p)
Access the balance factor of node p.
Definition avlNode.H:95
@ KEY
Definition btreepic.C:169
Inorder iterator on the nodes of a binary tree.
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
AVL balanced binary search tree.
Definition tpl_avl.H:109
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_avl.H:533
static Node * restore_avl(Node *p, Node *pp) noexcept
Definition tpl_avl.H:360
void restore_avl_after_insertion(Node *p) noexcept
Definition tpl_avl.H:381
void swap(Gen_Avl_Tree &tree) noexcept
Swap in constant time all the items of this with the items of tree.
Definition tpl_avl.H:517
Node * search_or_insert(Node *p) noexcept
Search or insert a key.
Definition tpl_avl.H:598
constexpr Compare & get_compare() noexcept
Definition tpl_avl.H:503
Node * insert_dup(Node *p) noexcept
Insert the node p without testing for key duplicity.
Definition tpl_avl.H:621
constexpr Node *& getRoot() noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:526
virtual ~Gen_Avl_Tree() noexcept
Definition tpl_avl.H:523
Node * search_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avl.H:146
static Node * doubleRotateLeft(Node *p) noexcept
Definition tpl_avl.H:260
Node * insert(Node *p) noexcept
Insert the node pointed by p in the tree.
Definition tpl_avl.H:561
Node * swapWithSuccessor(Node *p, Node *&pp) noexcept
Definition tpl_avl.H:422
Gen_Avl_Tree(Compare _cmp=Compare()) noexcept
Definition tpl_avl.H:505
void clean_avl_stack() noexcept
Definition tpl_avl.H:127
bool verify() const noexcept
Definition tpl_avl.H:694
NodeType< Key > Node
Definition tpl_avl.H:111
Node * search_dup_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avl.H:192
static Rotation_Type rotation_type(Node *p) noexcept
Definition tpl_avl.H:339
Node * remove(const Key &key) noexcept
Remove from an AVL tree the node containing key key.
Definition tpl_avl.H:640
constexpr Node * getRoot() const noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:529
constexpr Compare & key_comp() noexcept
The key type.
Definition tpl_avl.H:500
static Node * doubleRotateRight(Node *p) noexcept
Definition tpl_avl.H:297
bool avl_stack_at_base() noexcept
Definition tpl_avl.H:120
void restore_avl_after_deletion(bool left_deficit) noexcept
Definition tpl_avl.H:471
static Node * rotateRight(Node *p) noexcept
Definition tpl_avl.H:240
static Node * rotateLeft(Node *p) noexcept
Definition tpl_avl.H:220
FixedStack< Node * > avl_stack
The type of node.
Definition tpl_avl.H:114
Strict weak ordering constraint for BST comparators.
Definition ah-concepts.H:84
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
@ CmpGreater
First argument is greater than second.
@ CmpLess
First argument is less than second.
@ CmpEqual
Arguments are equal.
AVL binary search tree with virtual destructor in its nodes.
Definition tpl_avl.H:760
AVL binary search tree with nodes without virtual destructor.
Definition tpl_avl.H:737
Iterator over the nodes.
Definition tpl_avl.H:708
Iterator() noexcept=default
Default constructor creates an "end" iterator.
Iterator(const Gen_Avl_Tree &tree)
Definition tpl_avl.H:714
#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.