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
60using namespace Aleph;
61
62namespace Aleph
63{
105 template <template <typename> class NodeType, typename Key, class Compare>
107 {
108 public:
110
111 private:
116 Compare cmp;
117
119
121
122 Node * search_and_stack_avl(const Key & key,
123 signed char & cmp_result) noexcept
124 {
126
127 Node *p = root;
128 Node *candidate = nullptr; // Tracks potential duplicate when going right
129 size_t candidate_pos = 0; // Stack size when candidate was set
130
131 do // descend searching for key and push the search path
132 {
133 avl_stack.push(p);
134 if (cmp(key, KEY(p))) // key < KEY(p)
135 {
137 p = LLINK(p);
138 // Prefetch only the child we'll visit next iteration
139 if (p != Node::NullPtr) [[likely]]
141 }
142 else // key >= KEY(p), could be equal
143 {
144 candidate = p;
147 p = RLINK(p);
148 // Prefetch only the child we'll visit next iteration
149 if (p != Node::NullPtr) [[likely]]
151 }
152 }
153 while (p != Node::NullPtr);
154
155 // Optimistic duplicate check: when going right, key >= KEY(candidate)
156 // If also KEY(candidate) >= key (i.e., not less), then key == KEY(candidate)
157 if (candidate != nullptr and not cmp(KEY(candidate), key)) [[unlikely]]
158 {
160 // Truncate stack: remove nodes pushed after candidate
162 return candidate;
163 }
164
165 return avl_stack.top();
166 }
167
169 signed char & cmp_result) noexcept
170 {
173
174 Node *p = root;
175 do // descend searching for key and push the search path
176 {
177 avl_stack.push(p);
178 if (cmp(key, KEY(p))) // key < KEY(p)?
179 {
181 p = LLINK(p);
182 // Prefetch only the child we'll visit next iteration
183 if (p != Node::NullPtr) [[likely]]
185 }
186 else // key >= KEY(p)
187 {
189 p = RLINK(p);
190 // Prefetch only the child we'll visit next iteration
191 if (p != Node::NullPtr) [[likely]]
193 }
194 }
195 while (p != Node::NullPtr);
196
197 return avl_stack.top();
198 }
199
200 [[gnu::always_inline]] static Node * rotateLeft(Node *p) noexcept
201 {
202 assert(DIFF(p) == 2);
203 assert(RLINK(p) != Node::NullPtr);
204
205 Node *q = RLINK(p);
206 RLINK(p) = LLINK(q);
207 LLINK(q) = p;
208
209 if (DIFF(q) == 0) // balance factors adjustment
210 { // this case happens during deletion
211 DIFF(q) = -1;
212 DIFF(p) = 1;
213 }
214 else
215 DIFF(q) = DIFF(p) = 0;
216
217 return q;
218 }
219
220 [[gnu::always_inline]] static Node * rotateRight(Node *p) noexcept
221 {
222 assert(DIFF(p) == -2);
223 assert(LLINK(p) != Node::NullPtr);
224
225 Node *q = LLINK(p);
226 LLINK(p) = RLINK(q);
227 RLINK(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 * doubleRotateLeft(Node *p) noexcept
241 {
242 assert(DIFF(p) == 2 or DIFF(p) == -2);
243 assert(RLINK(p) != Node::NullPtr and LLINK(RLINK(p)) != Node::NullPtr);
244
245 Node *q = RLINK(p);
246 Node *r = LLINK(q);
247 RLINK(p) = LLINK(r);
248 LLINK(q) = RLINK(r);
249 LLINK(r) = p;
250 RLINK(r) = q;
251
252 unsigned char b; // logical height of r's left child
253 unsigned char c; // logical height of r's right child
254
255 // Determine logical heights of p, q and r
256 if (DIFF(r) == 1) // ==> c > b ==> c-b == 1
257 {
258 c = 1;
259 b = 0;
260 }
261 else if (DIFF(r) == -1) // ==> c < b ==> c-b = -1
262 {
263 c = 0;
264 b = 1;
265 }
266 else
267 c = b = 1;
268
269 // balance factors adjustment
270 DIFF(r) = 0;
271 DIFF(p) = b - 1; // logical height of p's left child is 1
272 DIFF(q) = 1 - c; // logical height of q's right child is 1
273
274 return r;
275 }
276
277 [[gnu::always_inline]] static Node * doubleRotateRight(Node *p) noexcept
278 {
279 assert(DIFF(p) == 2 or DIFF(p) == -2);
280 assert(LLINK(p) != Node::NullPtr and RLINK(LLINK(p)) != Node::NullPtr);
281
282 Node *q = LLINK(p);
283 Node *r = RLINK(q);
284 LLINK(p) = RLINK(r);
285 RLINK(q) = LLINK(r);
286 RLINK(r) = p;
287 LLINK(r) = q;
288
289 unsigned char b; // logical height of r's left child
290 unsigned char c; // logical height of r's right child
291
292 // determine logical heights of children of p, q and r
293 if (DIFF(r) == 1) // ==> c > b ==> c-b == 1
294 {
295 c = 1;
296 b = 0;
297 }
298 else if (DIFF(r) == -1) // ==> c < b ==> c-b == -1
299 {
300 c = 0;
301 b = 1;
302 }
303 else
304 c = b = 1;
305
306 // balance factors adjustment
307 DIFF(r) = 0;
308 DIFF(p) = 1 - c; // logical height of p's right child is 1
309 DIFF(q) = b - 1; // logical height of p's left child is 1
310
311 return r;
312 }
313
318
319 static Rotation_Type rotation_type(Node *p) noexcept
320 {
321 assert(DIFF(p) == 2 or DIFF(p) == -2);
322
323 Node *pc; // saves p's child
324 if (DIFF(p) == 2) // to the left
325 {
326 pc = RLINK(p);
327 if (DIFF(pc) == 1 or DIFF(pc) == 0)
328 return ROTATE_LEFT;
329
330 return DOUBLE_ROTATE_LEFT;
331 }
332
333 pc = LLINK(p);
334 if (DIFF(pc) == -1 or DIFF(pc) == 0)
335 return ROTATE_RIGHT;
336
337 return DOUBLE_ROTATE_RIGHT;
338 }
339
340 static Node * restore_avl(Node *p, Node *pp) noexcept
341 {
342 assert(LLINK(pp) == p or RLINK(pp) == p);
343 assert(DIFF(p) == -2 or DIFF(p) == 2);
344
345 Node **link = LLINK(pp) == p ? &LLINK(pp) : &RLINK(pp);
346 switch (rotation_type(p))
347 {
348 case ROTATE_LEFT: return *link = rotateLeft(p);
349 case ROTATE_RIGHT: return *link = rotateRight(p);
350 case DOUBLE_ROTATE_LEFT: return *link = doubleRotateLeft(p);
351 case DOUBLE_ROTATE_RIGHT: return *link = doubleRotateRight(p);
352
353 default:
354 AH_ERROR("Invalid rotation type");
355 break;
356 }
357
358 return nullptr;
359 }
360
362 {
363 Node *pp = avl_stack.pop(); // parent of the inserted node
364 if (LLINK(pp) == p) // adjust parent's balance factor
365 --DIFF(pp);
366 else
367 ++DIFF(pp);
368
369 if (DIFF(pp) == 0)
370 { // in this case, the height of pp's ancestor does not increase
372 return;
373 }
374
375 if (avl_stack_empty())
376 return; // pp is the root
377 do // search a node whose balance factor becomes 0
378 {
379 Node *gpp = avl_stack.pop(); // parent of pp
380 // update balance factors
381 if (LLINK(gpp) == pp) // AH_ERROR if (Compare () (key, KEY(gpp)))
382 --DIFF(gpp);
383 else
384 ++DIFF(gpp);
385
386 if (DIFF(gpp) == 0)
387 break; // no rebalancing is needed
388 if (DIFF(gpp) == -2 or DIFF(gpp) == 2) // AVL violation?
389 { // yes ==> rebalancing is required
390 Node *ggpp = avl_stack.pop();
392 break;
393 }
394
395 pp = gpp; // AH_ERROR; add
396 }
397 while (not avl_stack_empty());
398
400 }
401
402 Node * swapWithSuccessor(Node *p, Node *& pp) noexcept
403 { // Reference to the stack top, since p will be swapped with its
404 // successor and the successor will take p's position in the stack
406
407 Node *fSucc = p; // successor's parent
408 Node *succ = RLINK(p); // search starts from RLINK(p)
409
410 avl_stack.push(succ);
411
412 // find the successor while updating the stack
413 while (LLINK(succ) != Node::NullPtr) // descend as far left as possible
414 {
415 fSucc = succ;
416 succ = LLINK(succ);
417 avl_stack.push(succ);
418 }
419
420 // update old stack entry occupied by p: it is equivalent to swapping
421 // the old top (before searching succ) with the current one
422 ref_to_stack_top = succ;
423 avl_stack.top() = p;
424 if (LLINK(pp) == p) // update pp's new child (successor)
425 LLINK(pp) = succ;
426 else
427 RLINK(pp) = succ;
428
429 LLINK(succ) = LLINK(p); // swap left subtrees
430 LLINK(p) = Node::NullPtr;
431 if (RLINK(p) == succ) // update right subtrees
432 { // successor is exactly p's right child
433 RLINK(p) = RLINK(succ);
434 RLINK(succ) = p;
435 pp = succ;
436 }
437 else
438 { // successor is the leftmost descendant of RLINK(p)
439 Node *succr = RLINK(succ);
440 RLINK(succ) = RLINK(p);
441 LLINK(fSucc) = p;
442 RLINK(p) = succr;
443 pp = fSucc;
444 }
445
446 DIFF(succ) = DIFF(p); // swap balance factors
447
448 return succ;
449 }
450
452 {
453 Node *pp = avl_stack.top(1); // parent of p
454 Node *ppp = avl_stack.popn(3); // remove from stack p, parent and grandparent
455 while (true)
456 { // update balance factors
457 if (left_deficit) // AH_ERROR Compare () (key, KEY(pp)))
458 ++DIFF(pp);
459 else
460 --DIFF(pp);
461
462 if (DIFF(pp) == -2 or DIFF(pp) == 2) // still valid?
463 pp = restore_avl(pp, ppp); // no
464
465 if (DIFF(pp) != 0 or pp == root)
466 break; // global tree height has not changed ==> stop
467
468 left_deficit = LLINK(ppp) == pp;
469 pp = ppp; // advance to next ancestor
470 ppp = avl_stack.pop();
471 }
472
474 }
475
476 public:
477 using key_type = Key;
478
480 [[nodiscard]] constexpr Compare &key_comp() noexcept { return cmp; }
481
483 [[nodiscard]] constexpr Compare &get_compare() noexcept { return key_comp(); }
484
485 Gen_Avl_Tree(Compare __cmp = Compare()) noexcept
486 : avl_stack(Node::MaxHeight), head_ptr(&head_node),
488 {
490 }
491
497 void swap(Gen_Avl_Tree & tree) noexcept
498 {
499 std::swap(root, tree.root);
500 std::swap(cmp, tree.cmp);
501 }
502
504
506 [[nodiscard]] constexpr Node *&getRoot() noexcept { return root; }
507
509 [[nodiscard]] constexpr Node * getRoot() const noexcept { return root; }
510
513 [[nodiscard]] Node * search(const Key & key) const noexcept
514 {
516 }
517
529 [[nodiscard]] Node * insert(Node *p) noexcept
530 {
531 if (root == Node::NullPtr)
532 return root = p;
533
534 signed char cmp_result = CmpEqual;
536 if (cmp_result == CmpLess)
537 LLINK(pp) = p;
538 else if (cmp_result == CmpGreater)
539 RLINK(pp) = p;
540 else
541 { // duplicated key
543 return nullptr;
544 }
545
547
548 return p;
549 }
550
567 {
568 if (root == Node::NullPtr)
569 return root = p;
570
571 signed char cmp_result = CmpEqual;
573 if (cmp_result == CmpLess)
574 LLINK(pp) = p;
575 else if (cmp_result == CmpGreater)
576 RLINK(pp) = p;
577 else
578 { // duplicated key
580 return pp;
581 }
582
584
585 return p;
586 }
587
589 [[nodiscard]] Node * insert_dup(Node *p) noexcept
590 {
591 if (root == Node::NullPtr)
592 return root = p;
593
594 signed char cmp_result = CmpEqual;
596 if (cmp_result == CmpLess)
597 LLINK(pp) = p;
598 else
599 RLINK(pp) = p;
600
602
603 return p;
604 }
605
608 [[nodiscard]] Node * remove(const Key & key) noexcept
609 {
610 if (root == Node::NullPtr)
611 return nullptr;
612
613 signed char cmp_result = CmpEqual;
615 if (cmp_result != CmpEqual)
616 { // key was not found
618 return nullptr;
619 }
620
621 Node *pp = avl_stack.top(1); // get parent of p
622 bool left_deficit; // AH_ERROR Key removed_key = KEY(p);
623 while (true)
624 {
625 left_deficit = LLINK(pp) == p;
626 if (LLINK(p) == Node::NullPtr) // missing left child?
627 { // yes: link pp to p's child
628 if (LLINK(pp) == p)
629 LLINK(pp) = RLINK(p);
630 else
631 RLINK(pp) = RLINK(p);
632 break;
633 }
634
635 if (RLINK(p) == Node::NullPtr) // missing right child?
636 { // yes: link pp to p's child
637 if (LLINK(pp) == p)
638 LLINK(pp) = LLINK(p);
639 else
640 RLINK(pp) = LLINK(p);
641 break;
642 }
643
644 // here p is a full node ==> swap with successor
646 // removed_key = KEY(succ); // AH_ERROR remove
647 }
648
649 p->reset();
650
651 if (pp == head_ptr) // check if the root was removed
652 { // balance factors unchanged ==> AVL condition is not violated
654 return p;
655 }
656
658
659 return p;
660 }
661
663 {
664 return is_avl(root);
665 }
666
685 };
686
687
702 template <typename Key, class Compare = Aleph::less<Key>>
703 struct Avl_Tree : public Gen_Avl_Tree<AvlNode, Key, Compare>
704 {
706 using Base::Base;
707 };
708
709
724 template <typename Key, class Compare = Aleph::less<Key>>
725 struct Avl_Tree_Vtl : public Gen_Avl_Tree<AvlNodeVtl, Key, Compare>
726 {
728 using Base::Base;
729 };
730} // end namespace Aleph
731# endif // TPL_AVL_H
#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 & push(const T &data) noexcept
Push a copy of data
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.
T & top() const noexcept
Return a modifiable referemce to stack's top.
T pop() noexcept
Pop by moving the top of stack.
AVL balanced binary search tree.
Definition tpl_avl.H:107
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:513
static Node * restore_avl(Node *p, Node *pp) noexcept
Definition tpl_avl.H:340
void restore_avl_after_insertion(Node *p) noexcept
Definition tpl_avl.H:361
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:497
Node * search_or_insert(Node *p) noexcept
Search or insert a key.
Definition tpl_avl.H:566
constexpr Compare & get_compare() noexcept
Definition tpl_avl.H:483
Node * insert_dup(Node *p) noexcept
Insert the node p without testing for key duplicity.
Definition tpl_avl.H:589
constexpr Node *& getRoot() noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:506
bool avl_stack_empty() noexcept
Definition tpl_avl.H:118
virtual ~Gen_Avl_Tree() noexcept
Definition tpl_avl.H:503
Node * search_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avl.H:122
static Node * doubleRotateLeft(Node *p) noexcept
Definition tpl_avl.H:240
Node * insert(Node *p) noexcept
Insert the node pointed by p in the tree.
Definition tpl_avl.H:529
Node * swapWithSuccessor(Node *p, Node *&pp) noexcept
Definition tpl_avl.H:402
void clean_avl_stack() noexcept
Definition tpl_avl.H:120
bool verify() const noexcept
Definition tpl_avl.H:662
NodeType< Key > Node
Definition tpl_avl.H:109
Node * search_dup_and_stack_avl(const Key &key, signed char &cmp_result) noexcept
Definition tpl_avl.H:168
static Rotation_Type rotation_type(Node *p) noexcept
Definition tpl_avl.H:319
Gen_Avl_Tree(Compare __cmp=Compare()) noexcept
Definition tpl_avl.H:485
Node * remove(const Key &key) noexcept
Remove from an AVL tree the node containing key key.
Definition tpl_avl.H:608
constexpr Node * getRoot() const noexcept
Return a modifiable reference to tree's root.
Definition tpl_avl.H:509
constexpr Compare & key_comp() noexcept
The key type.
Definition tpl_avl.H:480
static Node * doubleRotateRight(Node *p) noexcept
Definition tpl_avl.H:277
void restore_avl_after_deletion(bool left_deficit) noexcept
Definition tpl_avl.H:451
static Node * rotateRight(Node *p) noexcept
Definition tpl_avl.H:220
static Node * rotateLeft(Node *p) noexcept
Definition tpl_avl.H:200
FixedStack< Node * > avl_stack
The type of node.
Definition tpl_avl.H:112
constexpr Node *& RLINK(Node *p) noexcept
Return the right tree of p.
constexpr Node *& LLINK(Node *p) noexcept
Return a pointer to left subtree.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
@ CmpGreater
First argument is greater than second.
@ CmpLess
First argument is less than second.
@ CmpEqual
Arguments are equal.
DynList< T > maps(const C &c, Op op)
Classic map operation.
AVL binary search tree with virtual destructor in its nodes.
Definition tpl_avl.H:726
AVL binary search tree with nodes without virtual destructor.
Definition tpl_avl.H:704
Iterator over the nodes.
Definition tpl_avl.H:676
Iterator() noexcept=default
Default constructor creates an "end" iterator.
Iterator(const Gen_Avl_Tree &tree)
Definition tpl_avl.H:682
Stack implementations backed by dynamic or fixed arrays.
Utility functions for binary tree operations.