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
50#ifndef TPL_AVL_H
51#define TPL_AVL_H
52
53#include <algorithm>
54#include <ahFunction.H>
55#include <tpl_arrayStack.H>
56#include <avlNode.H>
57#include <tpl_binNodeUtils.H>
58#include <ah-concepts.H>
59
60namespace Aleph {
102template <template <typename> class NodeType, typename Key, class Compare>
105{
106public:
108
109private:
114 Compare cmp;
115
120
122 {
123 if (avl_stack.is_empty())
124 {
126 return;
127 }
128
129 if (avl_stack.base() != head_ptr)
130 {
133 return;
134 }
135
136 if (const size_t to_pop = avl_stack.size() - 1; to_pop > 0)
137 avl_stack.popn(static_cast<int>(to_pop));
138 }
139
140 Node *search_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
141 {
143
144 Node *p = root;
145 Node *candidate = nullptr; // Tracks potential duplicate when going right
146 size_t candidate_pos = 0; // Stack size when candidate was set
147
148 do // descend searching for key and push the search path
149 {
152 avl_stack.push(p);
153 if (cmp(key, KEY(p))) // key < KEY(p)
154 {
156 p = LLINK(p);
157 }
158 else // key >= KEY(p), could be equal
159 {
160 candidate = p;
163 p = RLINK(p);
164 }
165 } while (p != Node::NullPtr);
166
167 // Optimistic duplicate check: when going right, key >= KEY(candidate)
168 // If also KEY(candidate) >= key (i.e., not less), then key == KEY(candidate)
169 if (candidate != nullptr and not cmp(KEY(candidate), key)) [[unlikely]]
170 {
172 // Truncate stack: remove nodes pushed after candidate
173 const size_t to_pop = avl_stack.size() > candidate_pos ? avl_stack.size() - candidate_pos : 0;
174 if (to_pop > 0)
175 avl_stack.popn(static_cast<int>(to_pop));
176 return candidate;
177 }
178
179 return avl_stack.top();
180 }
181
182 Node *search_dup_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
183 {
186
187 Node *p = root;
188 do // descend searching for key and push the search path
189 {
192 avl_stack.push(p);
193 if (cmp(key, KEY(p))) // key < KEY(p)?
194 {
196 p = LLINK(p);
197 }
198 else // key >= KEY(p)
199 {
201 p = RLINK(p);
202 }
203 } while (p != Node::NullPtr);
204
205 return avl_stack.top();
206 }
207
208 [[gnu::always_inline]] static Node *rotateLeft(Node *p) noexcept
209 {
210 assert(DIFF(p) == 2);
211 assert(RLINK(p) != Node::NullPtr);
212
213 Node *q = RLINK(p);
214 RLINK(p) = LLINK(q);
215 LLINK(q) = p;
216
217 if (DIFF(q) == 0) // balance factors adjustment
218 { // this case happens during deletion
219 DIFF(q) = -1;
220 DIFF(p) = 1;
221 }
222 else
223 DIFF(q) = DIFF(p) = 0;
224
225 return q;
226 }
227
228 [[gnu::always_inline]] static Node *rotateRight(Node *p) noexcept
229 {
230 assert(DIFF(p) == -2);
231 assert(LLINK(p) != Node::NullPtr);
232
233 Node *q = LLINK(p);
234 LLINK(p) = RLINK(q);
235 RLINK(q) = p;
236
237 if (DIFF(q) == 0) // balance factors adjustment
238 { // this case happens during deletion
239 DIFF(q) = 1;
240 DIFF(p) = -1;
241 }
242 else
243 DIFF(q) = DIFF(p) = 0;
244
245 return q;
246 }
247
248 [[gnu::always_inline]] static Node *doubleRotateLeft(Node *p) noexcept
249 {
250 assert(DIFF(p) == 2 or DIFF(p) == -2);
251 assert(RLINK(p) != Node::NullPtr and LLINK(RLINK(p)) != Node::NullPtr);
252
253 Node *q = RLINK(p);
254 Node *r = LLINK(q);
255 RLINK(p) = LLINK(r);
256 LLINK(q) = RLINK(r);
257 LLINK(r) = p;
258 RLINK(r) = q;
259
260 unsigned char b; // logical height of r's left child
261 unsigned char c; // logical height of r's right child
262
263 // Determine logical heights of p, q and r
264 if (DIFF(r) == 1) // ==> c > b ==> c-b == 1
265 {
266 c = 1;
267 b = 0;
268 }
269 else if (DIFF(r) == -1) // ==> c < b ==> c-b = -1
270 {
271 c = 0;
272 b = 1;
273 }
274 else
275 c = b = 1;
276
277 // balance factors adjustment
278 DIFF(r) = 0;
279 DIFF(p) = b - 1; // logical height of p's left child is 1
280 DIFF(q) = 1 - c; // logical height of q's right child is 1
281
282 return r;
283 }
284
285 [[gnu::always_inline]] static Node *doubleRotateRight(Node *p) noexcept
286 {
287 assert(DIFF(p) == 2 or DIFF(p) == -2);
288 assert(LLINK(p) != Node::NullPtr and RLINK(LLINK(p)) != Node::NullPtr);
289
290 Node *q = LLINK(p);
291 Node *r = RLINK(q);
292 LLINK(p) = RLINK(r);
293 RLINK(q) = LLINK(r);
294 RLINK(r) = p;
295 LLINK(r) = q;
296
297 unsigned char b; // logical height of r's left child
298 unsigned char c; // logical height of r's right child
299
300 // determine logical heights of children of p, q and r
301 if (DIFF(r) == 1) // ==> c > b ==> c-b == 1
302 {
303 c = 1;
304 b = 0;
305 }
306 else if (DIFF(r) == -1) // ==> c < b ==> c-b == -1
307 {
308 c = 0;
309 b = 1;
310 }
311 else
312 c = b = 1;
313
314 // balance factors adjustment
315 DIFF(r) = 0;
316 DIFF(p) = 1 - c; // logical height of p's right child is 1
317 DIFF(q) = b - 1; // logical height of p's left child is 1
318
319 return r;
320 }
321
329
330 static Rotation_Type rotation_type(Node *p) noexcept
331 {
332 assert(DIFF(p) == 2 or DIFF(p) == -2);
333
334 Node *pc; // saves p's child
335 if (DIFF(p) == 2) // to the left
336 {
337 pc = RLINK(p);
338 if (DIFF(pc) == 1 or DIFF(pc) == 0)
339 return ROTATE_LEFT;
340
341 return DOUBLE_ROTATE_LEFT;
342 }
343
344 pc = LLINK(p);
345 if (DIFF(pc) == -1 or DIFF(pc) == 0)
346 return ROTATE_RIGHT;
347
348 return DOUBLE_ROTATE_RIGHT;
349 }
350
351 static Node *restore_avl(Node *p, Node *pp) noexcept
352 {
353 assert(LLINK(pp) == p or RLINK(pp) == p);
354 assert(DIFF(p) == -2 or DIFF(p) == 2);
355
356 Node **link = LLINK(pp) == p ? &LLINK(pp) : &RLINK(pp);
357 switch (rotation_type(p))
358 {
359 case ROTATE_LEFT:
360 return *link = rotateLeft(p);
361 case ROTATE_RIGHT:
362 return *link = rotateRight(p);
364 return *link = doubleRotateLeft(p);
366 return *link = doubleRotateRight(p);
367
368 default:
369 AH_ERROR("Invalid rotation type");
370 break;
371 }
372
373 return nullptr;
374 }
375
377 {
378 Node *pp = avl_stack.pop(); // parent of the inserted node
379 if (LLINK(pp) == p) // adjust parent's balance factor
380 --DIFF(pp);
381 else
382 ++DIFF(pp);
383
384 if (DIFF(pp) == 0)
385 { // in this case, the height of pp's ancestor does not increase
387 return;
388 }
389
390 if (avl_stack_at_base())
391 return; // pp is the root
392 do // search a node whose balance factor becomes 0
393 {
394 Node *gpp = avl_stack.pop(); // parent of pp
395 // update balance factors
396 if (LLINK(gpp) == pp) // AH_ERROR if (Compare () (key, KEY(gpp)))
397 --DIFF(gpp);
398 else
399 ++DIFF(gpp);
400
401 if (DIFF(gpp) == 0)
402 break; // no rebalancing is needed
403 if (DIFF(gpp) == -2 or DIFF(gpp) == 2) // AVL violation?
404 { // yes ==> rebalancing is required
405 Node *ggpp = avl_stack.pop();
407 break;
408 }
409
410 pp = gpp; // AH_ERROR; add
411 } while (not avl_stack_at_base());
412
414 }
415
417 { // Reference to the stack top, since p will be swapped with its
418 // successor and the successor will take p's position in the stack
420
421 Node *fSucc = p; // successor's parent
422 Node *succ = RLINK(p); // search starts from RLINK(p)
423
424 avl_stack.push(succ);
425
426 // find the successor while updating the stack
427 while (LLINK(succ) != Node::NullPtr) // descend as far left as possible
428 {
429 fSucc = succ;
430 succ = LLINK(succ);
431 avl_stack.push(succ);
432 }
433
434 // update old stack entry occupied by p: it is equivalent to swapping
435 // the old top (before searching succ) with the current one
436 ref_to_stack_top = succ;
437 avl_stack.top() = p;
438 if (LLINK(pp) == p) // update pp's new child (successor)
439 LLINK(pp) = succ;
440 else
441 RLINK(pp) = succ;
442
443 LLINK(succ) = LLINK(p); // swap left subtrees
444 LLINK(p) = Node::NullPtr;
445 if (RLINK(p) == succ) // update right subtrees
446 { // successor is exactly p's right child
447 RLINK(p) = RLINK(succ);
448 RLINK(succ) = p;
449 pp = succ;
450 }
451 else
452 { // successor is the leftmost descendant of RLINK(p)
453 Node *succr = RLINK(succ);
454 RLINK(succ) = RLINK(p);
455 LLINK(fSucc) = p;
456 RLINK(p) = succr;
457 pp = fSucc;
458 }
459
460 DIFF(succ) = DIFF(p); // swap balance factors
461
462 return succ;
463 }
464
466 {
467 Node *pp = avl_stack.top(1); // parent of p
468 Node *ppp = avl_stack.popn(3); // remove from stack p, parent and grandparent
469 while (true)
470 { // update balance factors
471 if (left_deficit) // AH_ERROR Compare () (key, KEY(pp)))
472 ++DIFF(pp);
473 else
474 --DIFF(pp);
475
476 if (DIFF(pp) == -2 or DIFF(pp) == 2) // still valid?
477 pp = restore_avl(pp, ppp); // no
478
479 if (DIFF(pp) != 0 or pp == root)
480 break; // global tree height has not changed ==> stop
481
482 left_deficit = LLINK(ppp) == pp;
483 pp = ppp; // advance to next ancestor
484 ppp = avl_stack.pop();
485 }
486
488 }
489
490public:
491 using key_type = Key;
492
494 [[nodiscard]] constexpr Compare &key_comp() noexcept
495 {
496 return cmp;
497 }
498
500 [[nodiscard]] constexpr Compare &get_compare() noexcept
501 {
502 return key_comp();
503 }
504
505 Gen_Avl_Tree(Compare _cmp = Compare()) noexcept
507 {
509 }
510
516 void swap(Gen_Avl_Tree &tree) noexcept
517 {
518 std::swap(root, tree.root);
519 std::swap(cmp, tree.cmp);
520 }
521
523 {
525 }
526
528 [[nodiscard]] constexpr Node *&getRoot() noexcept
529 {
530 return root;
531 }
532
535 {
536 return root;
537 }
538
541 [[nodiscard]] Node *search(const Key &key) const noexcept
542 {
543 Node *p = root;
544 while (p != Node::NullPtr)
545 {
548 if (cmp(key, KEY(p)))
549 p = LLINK(p);
550 else if (cmp(KEY(p), key))
551 p = RLINK(p);
552 else
553 return p;
554 }
555 return nullptr;
556 }
557
569 [[nodiscard]] Node *insert(Node *p) noexcept
570 {
571 if (root == Node::NullPtr)
572 return root = p;
573
574 signed char cmp_result = CmpEqual;
576 if (cmp_result == CmpLess)
577 LLINK(pp) = p;
578 else if (cmp_result == CmpGreater)
579 RLINK(pp) = p;
580 else
581 { // duplicated key
583 return nullptr;
584 }
585
587
588 return p;
589 }
590
607 {
608 if (root == Node::NullPtr)
609 return root = p;
610
611 signed char cmp_result = CmpEqual;
613 if (cmp_result == CmpLess)
614 LLINK(pp) = p;
615 else if (cmp_result == CmpGreater)
616 RLINK(pp) = p;
617 else
618 { // duplicated key
620 return pp;
621 }
622
624
625 return p;
626 }
627
629 [[nodiscard]] Node *insert_dup(Node *p) noexcept
630 {
631 if (root == Node::NullPtr)
632 return root = p;
633
634 signed char cmp_result = CmpEqual;
636 if (cmp_result == CmpLess)
637 LLINK(pp) = p;
638 else
639 RLINK(pp) = p;
640
642
643 return p;
644 }
645
648 [[nodiscard]] Node *remove(const Key &key) noexcept
649 {
650 if (root == Node::NullPtr)
651 return nullptr;
652
653 signed char cmp_result = CmpEqual;
655 if (cmp_result != CmpEqual)
656 { // key was not found
658 return nullptr;
659 }
660
661 Node *pp = avl_stack.top(1); // get parent of p
662 bool left_deficit; // AH_ERROR Key removed_key = KEY(p);
663 while (true)
664 {
665 left_deficit = LLINK(pp) == p;
666 if (LLINK(p) == Node::NullPtr) // missing left child?
667 { // yes: link pp to p's child
668 if (LLINK(pp) == p)
669 LLINK(pp) = RLINK(p);
670 else
671 RLINK(pp) = RLINK(p);
672 break;
673 }
674
675 if (RLINK(p) == Node::NullPtr) // missing right child?
676 { // yes: link pp to p's child
677 if (LLINK(pp) == p)
678 LLINK(pp) = LLINK(p);
679 else
680 RLINK(pp) = LLINK(p);
681 break;
682 }
683
684 // here p is a full node ==> swap with successor
686 // removed_key = KEY(succ); // AH_ERROR remove
687 }
688
689 p->reset();
690
691 if (pp == head_ptr) // check if the root was removed
692 { // balance factors unchanged ==> AVL condition is not violated
694 return p;
695 }
696
698
699 return p;
700 }
701
703 {
704 return is_avl(root);
705 }
706
724};
725
740template <typename Key, class Compare = Aleph::less<Key>>
742struct Avl_Tree : public Gen_Avl_Tree<AvlNode, Key, Compare>
743{
745 using Base::Base;
746};
747
762template <typename Key, class Compare = Aleph::less<Key>>
764struct Avl_Tree_Vtl : public Gen_Avl_Tree<AvlNodeVtl, Key, Compare>
765{
767 using Base::Base;
768};
769} // end namespace Aleph
770#endif // TPL_AVL_H
C++20 concepts hub: comparison, BST policy, and Aleph container concepts.
#define AH_ERROR(...)
Print an error message (always enabled).
Definition ahDefs.H:270
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:105
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:541
static Node * restore_avl(Node *p, Node *pp) noexcept
Definition tpl_avl.H:351
void restore_avl_after_insertion(Node *p) noexcept
Definition tpl_avl.H:376
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:516
Node * search_or_insert(Node *p) noexcept
Search or insert a key.
Definition tpl_avl.H:606
constexpr Compare & get_compare() noexcept
Definition tpl_avl.H:500
Node * insert_dup(Node *p) noexcept
Insert the node p without testing for key duplicity.
Definition tpl_avl.H:629
constexpr Node *& getRoot() noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:528
virtual ~Gen_Avl_Tree() noexcept
Definition tpl_avl.H:522
Node * search_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avl.H:140
static Node * doubleRotateLeft(Node *p) noexcept
Definition tpl_avl.H:248
Node * insert(Node *p) noexcept
Insert the node pointed by p in the tree.
Definition tpl_avl.H:569
Node * swapWithSuccessor(Node *p, Node *&pp) noexcept
Definition tpl_avl.H:416
Gen_Avl_Tree(Compare _cmp=Compare()) noexcept
Definition tpl_avl.H:505
void clean_avl_stack() noexcept
Definition tpl_avl.H:121
bool verify() const noexcept
Definition tpl_avl.H:702
NodeType< Key > Node
Definition tpl_avl.H:107
Node * search_dup_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avl.H:182
static Rotation_Type rotation_type(Node *p) noexcept
Definition tpl_avl.H:330
Node * remove(const Key &key) noexcept
Remove from an AVL tree the node containing key key.
Definition tpl_avl.H:648
constexpr Node * getRoot() const noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:534
constexpr Compare & key_comp() noexcept
The key type.
Definition tpl_avl.H:494
static Node * doubleRotateRight(Node *p) noexcept
Definition tpl_avl.H:285
bool avl_stack_at_base() noexcept
Definition tpl_avl.H:116
void restore_avl_after_deletion(bool left_deficit) noexcept
Definition tpl_avl.H:465
static Node * rotateRight(Node *p) noexcept
Definition tpl_avl.H:228
static Node * rotateLeft(Node *p) noexcept
Definition tpl_avl.H:208
FixedStack< Node * > avl_stack
The type of node.
Definition tpl_avl.H:110
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
Main namespace for Aleph-w library functions.
Definition ah-arena.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.
AVL binary search tree with nodes with a virtual destructor.
Definition tpl_avl.H:765
AVL binary search tree with nodes without a virtual destructor.
Definition tpl_avl.H:743
Iterator over the nodes.
Definition tpl_avl.H:716
Iterator() noexcept=default
Default constructor creates an "end" iterator.
Iterator(const Gen_Avl_Tree &tree)
Definition tpl_avl.H:722
#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.