LAL: Linear Arrangement Library 21.07.01
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
avl.hpp
1/*********************************************************************
2 *
3 * Linear Arrangement Library - A library that implements a collection
4 * algorithms for linear arrangments of graphs.
5 *
6 * Copyright (C) 2019 - 2021
7 *
8 * This file is part of Linear Arrangement Library. To see the full code
9 * visit the webpage:
10 * https://github.com/lluisalemanypuig/linear-arrangement-library.git
11 *
12 * Linear Arrangement Library is free software: you can redistribute it
13 * and/or modify it under the terms of the GNU Affero General Public License
14 * as published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * Linear Arrangement Library is distributed in the hope that it will be
18 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with Linear Arrangement Library. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * Contact:
26 *
27 * LluĂ­s Alemany Puig (lalemany@cs.upc.edu)
28 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
29 * CQL (Complexity and Quantitative Linguistics Lab)
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://cqllab.upc.edu/people/lalemany/
32 *
33 * Ramon Ferrer i Cancho (rferrericancho@cs.upc.edu)
34 * LARCA (Laboratory for Relational Algorithmics, Complexity and Learning)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Office S124, Omega building
37 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
38 * Webpage: https://cqllab.upc.edu/people/rferrericancho/
39 *
40 ********************************************************************/
41
42#pragma once
43
44// C++ includes
45#if defined DEBUG
46#include <cassert>
47#endif
48#include <cinttypes>
49#include <vector>
50#include <cmath>
51
52namespace lal {
53namespace internal {
54
55template<class T>
56class AVL {
57public:
58 ~AVL() {
59 free_node(root);
60 root = nullptr;
61 }
62
63 [[nodiscard]]
64 size_t remove(const T& x) {
65 size_t top = 0;
66 root = remove(root, x, top);
67 return top;
68 }
69
70 // pre: -> v is sorted.
71 // -> v[0] > largest element of tree
72 void join_sorted_all_greater(const std::vector<T>& v) {
73 // do nothing if there is no data, duh
74 if (v.size() == 0) { return; }
75 // make the tree with the new info
76 tree_node *n =
77 _make_tree(
78 v, 0, static_cast<int64_t>(v.size() - 1), nullptr, '0'
79 );
80
81 // join the two trees
82
83 // if our root is empty then the new
84 // node is the root of the new tree
85 if (root == nullptr) {
86 root = n;
87 return;
88 }
89
90 // easy, degenerate cases
91 if (root->tree_size == 1) {
92 tree_node *r = insert(nullptr, n, '0', root->key);
93 free_node(root);
94 root = r;
95 }
96 else if (n->tree_size == 1) {
97 tree_node *r = insert(nullptr, root, '0', n->key);
98 free_node(n);
99 root = r;
100 }
101 else {
102 // complicated case
103 root = (root->height >= n->height ?
104 join_taller(root, n) :
105 join_shorter(root, n));
106 }
107
108 //sanity_check();
109 }
110
111private:
112 // ---------------------------------------------------------- //
113 // DEFINITIONS
114
115 struct tree_node {
116 // contents of the node
117 T key;
118
119 // side of this node:
120 // l: this node is a left subtree
121 // r: this node is a right subtree
122 // 0: this node is the root.
123 // Eq. parent = nullptr
124 char side = '0';
125
126 // Amount of nodes in the rooted at this node.
127 // Number of nodes in the left and right subtrees
128 // plus this node.
129 uint64_t tree_size = 0;
130 // Maximum height of the left and right subtrees' height
131 uint64_t height = 0;
132 // balance factor of a node:
133 // right subtree's height - left subtree's height
134 int64_t bf = 0;
135
136 // parent of this node
137 tree_node *parent = nullptr;
138 // left and right subtrees
139 tree_node *left = nullptr;
140 tree_node *right = nullptr;
141
142 void compute_height() noexcept {
143
144 const int64_t lh =
145 (left != nullptr ? static_cast<int64_t>(left->height) : -1);
146
147 const int64_t rh =
148 (right != nullptr ? static_cast<int64_t>(right->height) : -1);
149
150 height = static_cast<uint64_t>(std::max(lh, rh)) + 1;
151 bf = rh - lh;
152 }
153 void compute_size() noexcept {
154 const uint64_t ls = (left != nullptr ? left->tree_size : 0);
155 const uint64_t rs = (right != nullptr ? right->tree_size : 0);
156 tree_size = 1 + ls + rs;
157 }
158 void update() noexcept {
159 compute_size();
160 compute_height();
161 }
162
163 void link_parent_to(tree_node *n) {
164 if (n == nullptr) { return; }
165 if (parent != nullptr) {
166 if (side == 'l') {
167 parent->left = n;
168 }
169 else if (side == 'r') {
170 parent->right = n;
171 }
172 }
173 n->parent = parent;
174 n->side = side;
175 }
176
177 [[nodiscard]]
178 uint64_t left_size() const noexcept {
179 return (left == nullptr ? 0 : left->tree_size);
180 }
181 [[nodiscard]]
182 uint64_t right_size() const noexcept {
183 return (right == nullptr ? 0 : right->tree_size);
184 }
185 };
186
187private:
188 // ---------------------------------------------------------- //
189 // ATTRIBUTES
190
191 // root of the tree
192 tree_node *root = nullptr;
193
194private:
195 // ---------------------------------------------------------- //
196 // FUNCTIONS
197
198 void free_node(tree_node *n) {
199 if (n == nullptr) {
200 return;
201 }
202 free_node(n->left);
203 free_node(n->right);
204 delete n;
205 }
206
207 /* --------- */
208 /* ROTATIONS */
209
210 // rotations of nodes
211 // assume _n has a left subtree
212 [[nodiscard]]
213 tree_node *right_rotation(tree_node *_n) {
214#if defined DEBUG
215 assert(_n != nullptr);
216#endif
217
218 tree_node *A = _n;
219 tree_node *P = A->parent;
220 tree_node *B = A->left;
221
222#if defined DEBUG
223 assert(B != nullptr);
224#endif
225
226 // update A's parent
227 // (notice P might be null, however,
228 // it is null only when A->side != 'r' and 'l')
229 if (A->side == 'r') {
230 P->right = B;
231 }
232 else if (A->side == 'l') {
233 P->left = B;
234 }
235 // parent of B is now parent of A
236 B->parent = P;
237
238 // if A is the left child, then B is
239 // the left child, same for 'right'
240 B->side = A->side;
241
242 // update A's parent, and A's side
243 A->parent = B;
244 A->side = 'r';
245
246 // update node E
247 tree_node *E = B->right;
248 A->left = E;
249 if (E != nullptr) {
250 E->side = 'l';
251 E->parent = A;
252 }
253 B->right = A;
254
255 // update nodes A ...
256 A->update();
257 // ... and B
258 B->update();
259 return B;
260 }
261 // rotations of nodes
262 // assume _n has a right subtree
263 [[nodiscard]]
264 tree_node *left_rotation(tree_node *_n) {
265#if defined DEBUG
266 assert(_n != nullptr);
267#endif
268
269 tree_node *B = _n;
270 tree_node *A = B->right;
271
272 // parent of B is now parent of A
273 A->parent = B->parent;
274 // update A's parent
275 if (B->side == 'r') {
276 B->parent->right = A;
277 }
278 else if (B->side == 'l') {
279 B->parent->left = A;
280 }
281 // if A is the left child, then B is
282 // the left child, ...
283 A->side = B->side;
284
285 // update A's parent, and A's side
286 B->parent = A;
287 B->side = 'l';
288
289 // update node E
290 tree_node *E = A->left;
291 B->right = E;
292 if (E != nullptr) {
293 E->side = 'r';
294 E->parent = B;
295 }
296 A->left = B;
297
298 // update nodes B ...
299 B->update();
300 // ... and A
301 A->update();
302 return A;
303 }
304 // cases of imbalances:
305 // left_* cases assume that _n has left subtree
306 // right_* cases assume that _n has right subtree
307 [[nodiscard]]
308 tree_node *left_left_case(tree_node *n) {
309 return right_rotation(n);
310 }
311 [[nodiscard]]
312 tree_node *left_right_case(tree_node *n) {
313 n->left = left_rotation(n->left);
314 return right_rotation(n);
315 }
316 [[nodiscard]]
317 tree_node *right_right_case(tree_node *n) {
318 return left_rotation(n);
319 }
320 [[nodiscard]]
321 tree_node *right_left_case(tree_node *n) {
322 n->right = right_rotation(n->right);
323 return left_rotation(n);
324 }
325 // returns the root of the new balanced tree
326 [[nodiscard]]
327 tree_node *balance(tree_node *n) {
328 if (n == nullptr) { return nullptr; }
329#if defined DEBUG
330 assert(std::abs(n->bf) <= 2);
331#endif
332
333 if (std::abs(n->bf) <= 1) { return n; }
334 return (
335 n->bf == -2 ?
336 (n->left->bf <= 0 ? left_left_case(n) : left_right_case(n)) :
337 (n->right->bf >= 0 ? right_right_case(n) : right_left_case(n))
338 );
339
340 /*
341 if (n->bf == -2) {
342 // //if (n->left == nullptr) { return n; }
343
344
345 if (n->left->bf <= 0) {
346 return left_left_case(n);
347 }
348 return left_right_case(n);
349 }
350
351 //if (n->bf == +2) {
352 //if (n->right == nullptr) { return n; }
353
354 if (n->right->bf >= 0) {
355 return right_right_case(n);
356 }
357 return right_left_case(n);
358 //}
359 //return n;
360 */
361 }
362
363 /* --------------------- */
364 /* INSERTION OF ELEMENTS */
365
366 /* p: Parent of n.
367 * n: Reference to node.
368 * s: paren't side (0: root, l: left, r: right)
369 * x: Element to be added.
370 *
371 * Returns the newly created tree node
372 */
373 [[nodiscard]]
374 tree_node *insert(tree_node *p, tree_node *n, char s, const T& x) {
375 // create a new node
376 if (n == nullptr) {
377 n = new tree_node();
378
379 n->key = x;
380
381 n->parent = p;
382 n->left = nullptr;
383 n->right = nullptr;
384
385 n->side = s;
386
387 n->tree_size = 1;
388 n->height = 0;
389 n->bf = 0;
390 return n;
391 }
392
393 if (x == n->key) {
394 // do not insert already
395 // existing values
396 return n;
397 }
398
399 // insert as usual
400 if (x < n->key) {
401 n->left = insert(n, n->left, 'l', x);
402 }
403 else {
404 n->right = insert(n, n->right, 'r', x);
405 }
406
407 // update node size and height
408 n->update();
409 return balance(n);
410 }
411
412 /* ------------------- */
413 /* REMOVAL OF ELEMENTS */
414
415 [[nodiscard]]
416 tree_node *remove_leftmost(tree_node *n, T *k = nullptr) {
417 if (n->left == nullptr) {
418 // retrieve leftmost key
419 if (k != nullptr) {
420 *k = n->key;
421 }
422
423 tree_node *r = n->right;
424 n->link_parent_to(r);
425
426 delete n;
427 return r;
428 }
429 n->left = remove_leftmost(n->left, k);
430 n->update();
431 return balance(n);
432 }
433 [[nodiscard]]
434 tree_node *remove_rightmost(tree_node *n, T *k = nullptr) {
435 if (n->right == nullptr) {
436 // this is the rightmost
437 if (k != nullptr) {
438 *k = n->key;
439 }
440
441 tree_node *l = n->left;
442 n->link_parent_to(l);
443
444 delete n;
445 return l;
446 }
447
448 n->right = remove_rightmost(n->right, k);
449 n->update();
450 return balance(n);
451 }
452
453 // In a "general" implementation of an AVL tree, after a recursive call
454 // to 'remove', we may not want to continue doing work. Since this AVL
455 // tree is going to be used in a way that all 'x' this function is called
456 // with exist in the tree then we will always find this element in the
457 // tree, so there will be work to be done after each recursive call to
458 // 'remove'.
459 [[nodiscard]]
460 tree_node *remove(tree_node *n, const T& x, uint64_t& on_top) {
461 if (n == nullptr) {
462 // not found
463 on_top = 0;
464 return nullptr;
465 }
466
467 if (x < n->key) {
468 on_top += n->right_size() + 1;
469 n->left = remove(n->left, x, on_top);
470 // update this node's size
471 n->update();
472 // balance 'n' to keep the AVL invariant
473 return balance(n);
474 }
475 if (x > n->key) {
476 n->right = remove(n->right, x, on_top);
477 // update this node's size
478 n->update();
479 // balance 'n' to keep the AVL invariant
480 return balance(n);
481 }
482
483 // found element at node 'n'
484
485 // update amount of elements larger than 'x'
486 on_top += n->right_size();
487
488 // update tree
489 tree_node *L = n->left;
490 tree_node *R = n->right;
491 if (L == nullptr and R == nullptr) {
492 // nothing else to do, really
493 delete n;
494 return nullptr;
495 }
496 if (L != nullptr and R == nullptr) {
497 n->link_parent_to(L);
498 delete n;
499 // L is already balanced: not need to do that
500 return L;
501 }
502 if (L == nullptr and R != nullptr) {
503 n->link_parent_to(R);
504 delete n;
505 // R is already balanced: not need to do that
506 return R;
507 }
508
509 // L != nullptr and R != nullptr
510
511 // find the smallest value in the right subtree,
512 // or the largest in the left subtree,
513 // depending on the height
514
515 uint64_t dummy;
516 if (L->height > R->height) {
517 tree_node *lL = L;
518 while (lL->right != nullptr) {
519 lL = lL->right;
520 }
521 n->key = lL->key;
522 n->left = remove(L, lL->key, dummy);
523 }
524 else {
525 tree_node *sR = R;
526 while (sR->left != nullptr) {
527 sR = sR->left;
528 }
529 n->key = sR->key;
530 n->right = remove(R, sR->key, dummy);
531 }
532
533 // copy the key into n
534 n->update();
535 return balance(n);
536 }
537
538 /* ----------------- */
539 /* UNION OF TWO AVLS */
540
541 // L := largest key in T1
542 // S := smallest key in T2
543 // pre:
544 // -> height(T1) >= height(T2)
545 // -> L < S
546 [[nodiscard]]
547 tree_node *join_taller(tree_node *T1, tree_node *T2) {
548#if defined DEBUG
549 assert(T1 != nullptr);
550 assert(T2 != nullptr);
551 assert(T1->tree_size > 1 and T2->tree_size > 1);
552#endif
553
554 // we need a new node anyway
555 tree_node *x = new tree_node();
556#if defined DEBUG
557 assert(x != nullptr);
558#endif
559
560 // remove left-most element in T2
561 T2 = remove_leftmost(T2, &(x->key));
562 // find right-most node such that its height
563 // is either (T2->height) or (T2->height + 1)
564 // in T1
565 const uint64_t h = T2->height;
566 tree_node *v = T1;
567 uint64_t hp = v->height;
568 while (hp > h + 1 and v != nullptr) {
569 hp = (v->bf == -1 ? hp - 2 : hp - 1);
570 v = v->right;
571 }
572
573#if defined DEBUG
574 assert(v != nullptr);
575#endif
576
577 // NOTE: 'u' is allowed to be nullptr!
578 tree_node *u = v->parent;
579
580 x->side = '0';
581 x->parent = u;
582 x->left = v;
583 v->parent = x;
584 v->side = 'l';
585 x->right = T2;
586 T2->side = 'r';
587 T2->parent = x;
588 x->update();
589
590 // this is why 'u' is allowed to be nullptr!
591 if (u == nullptr) {
592 return balance(x);
593 }
594
595 u->right = x;
596 x->side = 'r';
597 x = balance(x);
598 // climb up the tree rebalancing
599 while (u->parent != nullptr) {
600 u->update();
601 u = balance(u);
602 u = u->parent;
603 }
604#if defined DEBUG
605 assert(u != nullptr);
606#endif
607
608 u->update();
609 return balance(u);
610 }
611 // L := largest key in T1
612 // S := smallest key in T2
613 // pre:
614 // -> height(T1) < height(T2)
615 // -> L < S
616 [[nodiscard]]
617 tree_node *join_shorter(tree_node *T1, tree_node *T2) {
618#if defined DEBUG
619 assert(T1 != nullptr);
620 assert(T2 != nullptr);
621 assert(T1->tree_size > 1 and T2->tree_size > 1);
622#endif
623
624 // we need a new node anyway
625 tree_node *x = new tree_node();
626#if defined DEBUG
627 assert(x != nullptr);
628#endif
629
630 // remove right-most element in T1
631 T1 = remove_rightmost(T1, &(x->key));
632 // find right-most node such that its height
633 // is either (T1->height) or (T1->height + 1)
634 // in T2
635 const uint64_t h = T1->height;
636 tree_node *v = T2;
637 uint64_t hp = v->height;
638 while (hp > h + 1 and v != nullptr) {
639 hp = (v->bf == -1 ? hp - 2 : hp - 1);
640 v = v->left;
641 }
642#if defined DEBUG
643 assert(v != nullptr);
644#endif
645
646 // NOTE: 'u' is allowed to be nullptr!
647 tree_node *u = v->parent;
648
649 x->side = '0';
650 x->parent = u;
651 x->right = v;
652 v->parent = x;
653 v->side = 'r';
654 x->left = T1;
655 T1->side = 'l';
656 T1->parent = x;
657 x->update();
658
659 // this is why 'u' is allowed to be nullptr!
660 if (u == nullptr) {
661 return balance(x);
662 }
663 u->left = x;
664 x->side = 'l';
665 x = balance(x);
666 // climb up the tree rebalancing
667 while (u->parent != nullptr) {
668 u->update();
669 u = balance(u);
670 u = u->parent;
671 }
672#if defined DEBUG
673 assert(u != nullptr);
674#endif
675
676 u->update();
677 return balance(u);
678 }
679
680 /* ------ */
681 /* OTHERS */
682
683 [[nodiscard]]
684 tree_node *_make_tree(
685 const std::vector<T>& v,
686 int64_t l, int64_t r, tree_node *p, char s
687 )
688 {
689 if (l > r) { return nullptr; }
690 const int64_t m = (l + r)/2;
691#if defined DEBUG
692 assert(m >= 0);
693#endif
694
695 // make a node with the element in the middle
696 tree_node *n = new tree_node();
697 n->key = v[static_cast<uint64_t>(m)];
698 // make sure pointers are correct
699 n->parent = p;
700 n->side = s;
701 // construct the tree making
702 // sure the invariant is kept
703 n->left = _make_tree(v, l, m - 1, n, 'l');
704 n->right = _make_tree(v, m + 1, r, n, 'r');
705 n->update();
706 // by construction, there is no
707 // need to balance the node 'n'
708 return n;
709 }
710};
711
712} // -- namespace internal
713} // -- namespace lal
Main namespace of the library.
Definition definitions.hpp:48