Abstract 1 Introduction 2 State-of-the-art 3 Efficient bottom-up updates 4 Top-down updates References

Efficient Top-Down Updates in AVL Trees

Vincent Jugé ORCID LIGM, CNRS, Univ Gustave Eiffel, Marne-la-vallée, France
IRIF, Université Paris-Cité & CNRS, France
Abstract

Since AVL trees were invented in 1962, two major open questions about rebalancing operations, which found positive answers in other balanced binary search trees, were left open: can these operations be performed top-down (with a fixed look-ahead), and can they use an amortised constant number of write operations per update? We propose an algorithm that solves both questions positively.

Keywords and phrases:
AVL trees, data structures, amortised complexity
Copyright and License:
[Uncaptioned image] © Vincent Jugé; licensed under Creative Commons License CC-BY 4.0
2012 ACM Subject Classification:
Theory of computation Sorting and searching
Editors:
Anne Benoit, Haim Kaplan, Sebastian Wild, and Grzegorz Herman

1 Introduction

Balanced search trees are among the most fundamental and basic data structures in computer science. Their formidable story started with the invention of AVL trees by Adel’son-Vel’skii and Landis [1] in 1962, who proposed a data structure and a maintenance algorithm thanks to which the insertion, deletion and research of an element in a linearly ordered set of size n was feasible in time 𝒪(log(n)).

AVL trees are binary search trees in which each node x maintains some form of balance: the heights of its children differ from each other by at most 1. Satisfying this property at each node ensures that the tree is of height 𝒪(log(n)). The number of comparisons required to search an element in a binary search tree being linear in the height of the tree, it is thus logarithmic in n.

However, for instance, inserting a node into the sub-tree rooted at the left child of our node x may increase the height of that sub-tree, damaging the balance of x. Thus, the challenge of maintenance algorithms consists in efficiently modifying the parenthood relations inside the tree in order to ensure that each node remains balanced.

Unfortunately, in spite of their excellent worst-case height, AVL trees suffer from updating algorithms that are significantly less efficient than those of other balanced binary search tree structures such as weight-balanced trees [9], red-black trees [4], half-balanced trees [11], splay trees [12] or, more recently, weak AVL trees [5]. Indeed, as illustrated in Table 1, they have two shortcomings, from which almost all other algorithms are exempt:

  • starting from an empty tree, performing n updates in a row may trigger Θ(nlog(n)) rotations [2] or, more generally, write operations, which may also consist in modifying the balance of a node without changing the tree structure;

  • updates following an insertion or deletion must be performed in a bottom-up fashion; the inventors of weak AVL trees even said, in [5, p. 23], that “Top-down insertion or deletion with fixed look-ahead in an AVL tree is problematic. (We do not know of an algorithm; we think there is none.)”

Table 1: Amortised number of rewrite operations per update with n nodes, and existence of top-down update algorithms. Next to each piece of information are indicated references where it is proved.
Tree family Amortised write cost/update Top-down update algorithm
AVL (state of the art) Θ(log(n)) [2] no
AVL (this article) Θ(1) yes
Weight-balanced Θ(1) [3] yes [7, 8]
Red-black Θ(1) [4] yes [4]
Half-balanced Θ(1) [11] for insertions [10]
Splay Θ(log(n)) [12] yes [12]
Weak AVL Θ(1) [5] yes [5]

Requiring only Θ(1) amortised write operations per update instead of Θ(log(n)) would clearly improve the efficiency of AVL trees. As mentioned in [4, 5], being able to use top-down algorithms is also important, because requiring a bottom-up pass is costly: either it forces each node to maintain a link toward its parent, or it must be performed via recursive calls or storing visited nodes in a stack. This second approach is even more expensive in distributed settings, because each process should lock the entire branch it is visiting, preventing any update from starting before the previous update has ended.

A way of circumventing these deficiencies of AVL trees is to use weak AVL trees instead [5]. By having less demanding balance criteria, this variant of AVL offers update algorithms that require 𝒪(1) rotations in the worst case, and 𝒪(1) amortised write operations (which may include updating information on the balance of nodes) per update. Furthermore, these algorithms can be made top-down, with the following exception, which is common to all binary search tree data structures: instead of deleting an internal node x, we must delete a node y whose key immediately precedes or follows the key of x, an approach called Hibbard deletion [6], and then replace the key of x by that of y. In AVL trees, y must be a leaf or the parent of a leaf, which reduces the problem of node deletion to that of leaf deletion, up to to keeping a pointer on an internal node we may wish to delete.

In this article, we prove that top-down insertion and deletion with fixed look-ahead in an AVL tree is feasible; having a look-ahead of d means that the algorithm, when it reads or writes information at a node x, is forbidden to later read or write informations at nodes that do not descend from the dth ancestor of x. At the same time, this can also be write-efficient, thus bridging a gap of theoretical importance: it was somewhat unsettling to observe that this seminal balanced tree structure was also the one for which no efficient algorithms had been found, thus leading specialists in this field to conjecture that such algorithms did not exist.

We proceed step-by-step: in Section 2, we briefly present textbook algorithms for updating AVL trees; in Section 3, we propose a bottom-up algorithm that requires 𝒪(1) amortised write operations per update; finally, in Section 4, we propose a top-down algorithm that requires 𝒪(1) amortised write operations per update. Note that, unlike algorithms for AVL trees or some other structures (e.g., red-black trees), a given update may require these algorithms to perform Θ(log(n)) rotations: our efficiency criterion is based on the amortised number of write operations (and, in particular, rotations) only, not on the maximal number of such operations that a single update may require.

Both algorithms have flavours similar to those used when designing weak AVL trees [5]: they rely on making sure that “transient” rebalancing operations decrease some integer-valued potential and, for the top-down algorithm, on being able to start and stop long enough yet finite sequences of such transient operations. This illustrates the usefulness of the approach used to design these algorithms, and also suggests that additional results similar to [5, Theorem 7.4] should also be valid here. The main difficulties, however, reside first in designing a suitable notion of potential, and then in managing to start and stop these sequences of transient operations; this task is easy when dealing with insertions only, and quite challenging when taking deletions into account.

2 State-of-the-art

2.1 Preliminaries

A binary search tree is an ordered rooted tree 𝒯 in which each tree node x has one key 𝗄𝖾𝗒(x), one left child x1 and one right child x2, which can be tree nodes or null nodes; null nodes represent the empty tree and have no key nor children, and tree nodes whose two children are null nodes are called leaves. By definition, the descendants of x consist of x, its children x1 and x2, and all their descendants (if they are tree nodes); these descendants form a sub-tree of 𝒯 rooted at x and denoted by 𝒯(x), and x is one of their ancestors.

Node keys are pairwise distinct, and must belong to a linearly ordered set. For each tree node y that descends from x, we have 𝗄𝖾𝗒(y)<𝗄𝖾𝗒(x) if y belongs to 𝒯(x1), and 𝗄𝖾𝗒(y)>𝗄𝖾𝗒(x) if y belongs to 𝒯(x2). Moreover, the height of x is the length (i.e., the number of parent-child edges) of the longest downward path starting from x and ending in a leaf; it is denoted by 𝗁(x). By convention, each null node has height 1.

A binary search tree is an AVL tree when the height difference 𝗁(x1)𝗁(x2) belongs to the set {1,0,1} for all tree nodes x. This ensures that an AVL tree with n nodes is of height 𝒪(log(n)).

Here, we follow the presentation of [5] and assume that AVL tree structures are implemented by assigning a rank 𝗋(x) to each tree node x. This rank is our current estimation of the height 𝗁(x), which may be slightly invalid if, say, some leaf was inserted way below x, and we did not yet have the time to update information about the height of x. Hence, we should control the rank differences 𝗋(x)𝗋(xi) for each node x. Focusing on these differences, we say that xi is an -child if 𝗋(x)𝗋(xi)=, and that x is an {,}-node if its children are - and -children.

In practice, these ranks are rarely stored directly in AVL implementations: instead, each node x contains either (i) the difference 𝗋(x2)𝗋(x1) between the ranks of its children x1 and x2, or (ii) the difference 𝗋(p)𝗋(x) between the rank of its parent p and its own rank. In what follows, we will disregard these implementation details, and pretend that we stored node ranks in clear; adapting our algorithm to let it fit the frameworks (i) or (ii) is easy.

Since binary search trees represented ordered sets, the three main operations they must support are searching, inserting, and deleting an element. Searching an element is easy: when searching a key k in a non-empty tree 𝒯(x), we compare k with 𝗄𝖾𝗒(x); then, we keep searching k in 𝒯(x1) if k<𝗄𝖾𝗒(x), or in 𝒯(x2) if k>𝗄𝖾𝗒(x), or we declare that k was found if k=𝗄𝖾𝗒(x); whereas, when searching k in an empty tree, we must declare that k was nowhere to be found.

Inserting or deleting an element is substantially more difficult, because it requires altering the structure of the tree: this may change its height and even damage the balance of some tree nodes. As a result, the relation 𝗋(x)=max{𝗋(x1),𝗋(x2)}+1 might fail for one node x, or one rank difference 𝗋(x1)𝗋(x2) might no longer belong to the set {1,0,1}. The purpose of bottom-up rebalancing algorithms is precisely to eliminate this anomaly, either directly, or by “propagating it upward”, i.e., making it concern a strict ancestor of x, with rank larger than 𝗋(x).

2.2 Tree cuts and cut rebalancing

A cut of a tree 𝒯 is a list (x1,x2,,x) of nodes of 𝒯 that are pairwise incomparable for the ancestorship relation (i.e., the sub-trees 𝒯(xi) are pairwise disjoint), ordered from left to right, and which is maximal for inclusion.

Let x denote the root of 𝒯. When the nodes x1,x2,,x have ranks 𝗋(x)δ1, 𝗋(x)δ2,, 𝗋(x)δ, the profile of the associated cut is defined as the integer tuple 𝜹=(δ1,δ2,,δ). Accordingly, we say that a node x has profile 𝜹 when 𝒯(x) contains a tree cut of profile 𝜹. When the order of the integers δ1,δ2,,δ is irrelevant, we may also say that x has profile {δ1,δ2,,δ}, just writing a multiset of integers: this just means that there exists a permutation σ of {1,2,,} such that x has profile (δσ(1),δσ(2),,δσ()). In particular, an {,}-node is simply a node with profile {,}.

Below, we may modify the structure of a tree 𝒯, thus generalising so-called rotations: starting from a cut (x1,x2,,x) of 𝒯, let a1,a2,,a1 be the strict ancestors of the cut nodes xi, listed from left to right. We assign a new pair of children to each node ai, and decide that the new root should be some node aj, as long as the graph we obtain is a binary search tree rooted at aj; nodes aj are said to be affected by the cut rebalancing. While doing so, (i) we must never modify the structure or rank of any of the sub-trees 𝒯(xi), and (ii) node ranks of nodes ai are reassigned to make sure that each node ai is a {1,1}- or a {1,2}-node in the resulting tree. Such an operation is called a cut rebalancing, and is easy to describe diagrammatically, like in Figure 1 below; cut nodes and their ancestors will typically be denoted with capital letters, from left to right.

2.3 Textbook bottom-up algorithm

Inserting a key may create a zero-edge, i.e., an edge between a parent x and its child xi such that 𝗋(x)=𝗋(xi); in that case, the integer 𝗋(x)=𝗋(xi) is called the rank of the zero-edge. A zero-edge is created when x is a leaf below which we wish to insert the key k, because some child xi of x, which used to be null, becomes a leaf.

Similarly, deleting a key may create a {2,2}- or {1,3}-node, which we call a four-node; this happens if x is a {1,2}-node one of whose children is a leaf containing the key k we wish to delete, because that leaf will be transformed into a null node.

Zero-edges are dealt with as follows, and as illustrated in Figure 1. Up to using a left-right symmetry, and without loss of generality, we assume that 𝗋(x1)𝗋(x2), i.e., that both 𝗋(x) and 𝗋(x1) are equal to the rank 𝗋 of the zero-edge:

Figure 1: Eliminating or propagating upward a (thick, red-painted) zero-edge of rank 𝗋 through cut rebalancings. Black octagons represent cut nodes, at which are rooted sub-trees whose structure and ranks will not change. White circles represent their ancestors. Once the rebalancing operation has taken place, the rank of a node is defined as the smallest integer larger than the ranks of its children. Below each node z is written the rank difference 𝗋𝗋(z); when two rank differences δ and δ are possible, we just write “ δorδ ”. These notations will be used in all subsequent diagrams.
  1. 1.

    If 𝗋(x11)=𝗋(x12)=𝗋1 and 𝗋(x2)=𝗋2 (row 1, left), the cut (x11,x12,x2) has profile (1,1,2). Let 𝖠, 𝖢 and 𝖤 denote the cut nodes x11, x12 and x2, and let 𝖡 and 𝖣 denote their ancestors x1 and x. We rebalance 𝒯(x) by letting 𝖣 become the parent of 𝖢 and 𝖤, and 𝖡 become the parent of 𝖠 and 𝖣, thus obtaining a tree with root 𝖡. This cut rebalancing is a simple rotation.

  2. 2.

    If 𝗋(x11)=𝗋1 and 𝗋(x12)=𝗋(x2)=𝗋2 (row 1, right), we perform the same rotation; however, the resulting ranks differ.

  3. 3.

    If 𝗋(x12)=𝗋1 and 𝗋(x11)=𝗋(x2)=𝗋2 (row 2, left), the cut (x11,x121,x122,x2) is to be rebalanced. Let 𝖠, 𝖢, 𝖤 and 𝖦 denote the cut nodes x11, x121, x122 and x2, and let 𝖡, 𝖣 and 𝖥 denote their ancestors x1, x12 and x. We rebalance 𝒯(x) by giving these seven nodes the shape of a complete binary tree: 𝖡 becomes the parent of 𝖠 and 𝖢, 𝖥 becomes the parent of 𝖤 and 𝖦, and 𝖣 becomes the parent of 𝖡 and 𝖥, as well as the root of the resulting tree. This is a double rotation.

  4. 4.

    If 𝗋(x2)=1 (row 2, right), we rebalance 𝒯(x) by not changing its structure, but simply increasing the rank of its root x. This is a promotion.

Doing so, whenever we face a zero-edge e of rank 𝗋, we will either eliminate it at once, or replace it by a new zero-edge e of rank 𝗋+1, placed just above where e used to be. Consequently, if inserting a key creates a zero-edge, we will simply propagate that zero-edge upward until it disappears.

Likewise, four-nodes are dealt with as follows, and as illustrated in Figure 2. Up to using a left-right symmetry, we assume that 𝗋(x1)𝗋(x2):

  1. 5.

    If 𝗋(x11)=𝗋(x12)=𝗋2 and 𝗋(x2)=𝗋3 (row 1, left), we perform a simple rotation.

  2. 6.

    If 𝗋(x11)=𝗋2 and 𝗋(x12)=𝗋(x2)=𝗋3 (row 1, right), we perform the same rotation; the resulting ranks differ.

  3. 7.

    If 𝗋(x12)=𝗋2 and 𝗋(x11)=𝗋(x2)=𝗋3 (row 2, left), we perform a double rotation.

  4. 8.

    If 𝗋(x2)=𝗋2 (row 2, right), we decrease the rank of its root x; this is a demotion.

Figure 2: Eliminating or increasing the rank of a (thick, red-painted) four-node.

Cut rebalancing operations 5 to 7 are the same as operations 1 to 3 for eliminating zero-edges, except that the ranks of x1, x2 and their descendants were all shifted down by one. In all eight cases, these operations result either in eliminating the four-node x or in making the (former) parent of x our new four-node.

3 Efficient bottom-up updates

3.1 Overview and complexity analysis

The textbook algorithm presented in Section 2.3 aims at eliminating or propagating upward zero-edges and four-nodes. Thus, each insertion or deletion update consists in (i) finding the location at which we want to create or delete a tree leaf, (ii) observe that we may have created a zero-edge or four-node, (iii) moving this anomaly upward, until (iv) we eliminate it once and for all. Step (i) requires no write operation on the tree structure, and steps (ii) and (iv) occur once per update. However, step (iii) may require arbitrarily many write operations per update.

Hence, cut rebalancing operations of cases 1 to 8 are called terminal if they result in eliminating the anomaly (thus being a part of step (iv)), and transient if they just propagate it upward (thus being a part of step (iii)): transient operations are operations 1 and 4 when x used to be a 1-child, and operations 6 to 8 when x had a parent that used to be a {1,2}-node. We should prove that updating n times an initially empty AVL tree triggers 𝒪(n) transient operations.

With the textbook algorithm, the statement we wish to prove is invalid, as was shown in [2]. Consequently, we will tweak the operations performed in cases 1 to 8, aiming at making them terminal whenever possible.

The complexity proof of the resulting algorithm is based on potential techniques. In our case, we define the potential of a a tree 𝒯 as the the number of {1,2}-nodes, plus twice the number of full nodes it contains, where full nodes are defined as follows.

In absence of zero-edges, a node x is called full when x and its two children x1 and x2 are {1,1}-nodes, and at least one of its grand-children x12 and x21 is a {1,1}-node. In other words, a node is full when it has profile (2,3,3,2,2) or, symmetrically, (2,2,3,3,2). When zero-edges are (temporarily) allowed, a node x may also be considered full if x is a {0,1}-node whose 1-child is a {1,1}-node; or if x is a {1,1}-node, x1 and x2 are {0,1}- and/or {1,1}-nodes, and one of x12 and x21 is a 0-child or a null, a {0,1}- or a {1,1}-node.

As we will prove:

  1. 1.

    each update or write operation modifies the neighbourhood (parenthood relations and/or rank) of a constant number of nodes, thus increasing the tree potential by at most a constant;

  2. 2.

    each transient operation aimed at eliminating a zero-edge of rank 𝗋4 destroys one full node and creates one {1,2}-node, thus decreasing the tree potential;

  3. 3.

    each transient operation triggered by a deletion decreases the number of {1,2}-nodes, and does not increase the number of full nodes, also decreasing the tree potential.

Statement 1 will be immediate, and thus we will focus on proving statement 2 in Section 3.3 (this will be Lemma 3), and statement 3 in Section 3.4 (this will be Lemma 4). Once all three statements are proved, we will finally be able to derive the following result.

Theorem 1.

The updating algorithm that we present in Sections 3.3 (for insertions) and 3.4 (for deletions) performs 𝒪(1) amortised write operations per update.

Proof.

Starting from an empty tree, let n insertion or deletion updates be performed. Let us keep track of the variations of tree potential while these updates are performed. There exists a constant 𝖪 such that each update requires:

  1. 1.

    inserting or deleting a leaf, an operation that increases the potential by at most 𝖪;

  2. 2.

    eliminating at most four zero-edges of rank 𝗋3, each of which increases the potential by at most 𝖪;

  3. 3.

    performing a certain number of transient operations, each of which decreases the potential by at least one;

  4. 4.

    performing at most one terminal operation, which increases the potential by at most 𝖪.

Hence, if 𝖳 transient operations were performed in total, the potential increased by at most 6n𝖪𝖳. It started with the value 0, and ended with a non-negative value, which shows that 𝖳6n𝖪. Therefore, in total, these n updates required performing no more than 𝒪(n)+𝒪(𝖳)𝒪(n) write operations.

3.2 Cut promotions and demotions

Some cut profiles, such as the ones that charaterise full nodes, allow rebalancing trees to either increase or decrease their rank. More precisely, if we are lucky enough, a tree 𝒯 with rank 𝗋 and profile 𝜹 will always admit a cut, whose profile does not need to be 𝜹, that can be rebalanced to obtain a tree 𝒯 of rank 𝗋=𝗋+ε, where ε=±1 is an integer of our choice. This cut rebalancing operation is called a cut promotion when ε=1, and a cut demotion when ε=1.

Some profiles ensure that a cut promotion or demotion can be performed, as outlined by the following result.

Lemma 2.

Trees with a full root can always be subject to a cut promotion; the root of the resulting tree is a {1,2}-node. By contrast, trees with profile {3,3,3,4,4} can always be subject to a cut demotion; the root of the resulting tree is a {1,1}-node.

Proof.

The recipe given in Figure 3 (row 1) shows how to apply a cut promotion to a tree whose root is full. Hence, let us focus on applying a cut demotion on a tree 𝒯 that admits a cut (x1,x2,x3,x4,x5) with profile {3,3,3,4,4}.

Replacing the three nodes xi with rank 𝗋3 by their children gives an 8-node cut, whose nodes all have rank 𝗋4 or 𝗋5, at least five of these nodes having rank 𝗋4. These 8 nodes and their 7 ancestors can be reshaped to form a 15-node complete binary tree 𝒯 of height 3, whose nodes at height h must have rank 𝗋5+h or 𝗋4+h. Moreover, among the four leaves (y1,y2,y3,y4), at least one has rank 𝗋4, which proves that the root x of 𝒯 has a left child x1 of rank 𝗋1; similarly, its right child has rank 𝗋2, and x is a {1,1}-node of rank 𝗋1. This is illustrated in Figure 3 (row 2).

In general, when cut-rebalancing a k-node cut transforms a sub-tree 𝒯(x) into a tree 𝒯 with root x and rank 𝗋(x)1, 𝗋(x) or 𝗋(x)+1, the tree potential increases by no more than 2k+4. Indeed, k1 cut ancestors were affected, possibly becoming {1,2}-nodes or full nodes; the parent of x, whose child is now x, might also become a {1,2}-node or a full node; and the grand-parent and great-grand-parent of x might become full nodes. Hence, cut promotions increase the tree potential by 14 or less, and cut demotions increase this potential by 20 or less.

Figure 3: Row 1: Applying a cut promotion to a tree whose root is full. Row 2: 15-node complete binary tree whose root has profile {1,1} and rank 𝗋1, which results from applying a cut demotion to a tree with profile {3,3,3,4,4}.

These two kinds of cut promotions and demotions will be used in Sections 3.3 and 3.4. Another kind of cut rebalancing operation, called saving, will be introduced in Section 4.3, only being useful for designing efficient top-down updating algorithms.

3.3 Insertion

Let us modify the textbook insertion algorithm to make some transient operations, which should occur in cases 1 and 4 of page 1, terminal operations. In particular, all transient operations in our variant are already transient in the textbook algorithm.

Case 1 is easy to take care of. Indeed, let us say that a node x is weak when x is either a leaf or a {1,2}-node whose 1-child is also weak. An immediate induction proves that, when using the textbook algorithm, the lower end of a zero-edge is always weak. This proves, in particular, the well-known fact that case 1 is actually spurious, since it features a zero-edge whose lower end is a {1,1}-node.

It remains to take care of case 4, which we subdivide into two sub-cases. First, we promote x like in the textbook algorithm, thus obtaining a tree 𝒯 of rank 𝗋+1. Then,

  1. 4.
    1. a)

      if x2 is a {1,1}-node, we do nothing more: we just performed a node promotion;

    2. b)

      if x2 is a {1,2}-node, let us recall that x1 is weak, which gives it the profile {2,2,3}, whereas x2 has profile {1,2}; hence, 𝒯 has profile {3,3,3,4,4}, and a cut demotion transforms 𝒯 into a tree 𝒯′′ of rank 𝗋 again: we just performed a terminal operation.

Lemma 3.

Each transient operation aimed at eliminating a zero-edge of rank 𝗋4 destroys one full node and creates one {1,2}-node, thus decreasing the tree potential.

Proof.

We just saw that the only transient case is case 4a, on the condition that x was a 1-child.

If the parent x(1) of x is full after x was promoted, it must now be a {0,1}-node whose other child is a {1,1}-child. Hence, before promoting x, its parent x(1) was already a {1,1}-node whose children were a {0,1}-and a {1,1}-node, its child x having two children x1 and x2 that were a 0-child and a {1,1}-node. This means that x(1) was already full.

Similarly, if the grand-parent x(2) of x is full after x was promoted, it must now be a {1,1}-node whose children are a {1,0}-node (the node x(1)) and a {1,1}-node (the sibling of x(1)), one of it grand-children x12(2) or x21(2), say y, being a 0-child or a {1,1}-node. Before promoting x, the node x(1) was already a {1,1}-node, and either y=x, in which case y was already a {0,1}-node, or yx, in which case y was already a {1,1}-node. This means that x(2) was already full.

Finally, promoting x, in case it used to be a 1-child, does not make any other ancestor of x a full node. Hence, our transient operation transformed a full node into a {1,2}-node, and did not create any other full node or {1,2}-node: as a result, it decreased the tree potential by one.

3.4 Deletion

Similarly, we wish to transform as many transient operations, which occur in cases 6 to 8 of the textbook textbook algorithm, into terminal operations.

The rotations and demotion performed in these cases transform a tree 𝒯(x) of rank 𝗋 rooted at a four-node x into a tree 𝒯 of rank 𝗋1 whose root x is a {1,1}-node. If x is full, performing a cut promotion on 𝒯 results in another tree 𝒯′′ of rank 𝗋; similarly, if some child xi of x is full, performing a cut promotion on 𝒯(xi) and increasing the rank of x by 1 also transforms 𝒯 into another tree 𝒯′′ of rank 𝗋; and if both children of x are full, x is also full, a case we already took care of. In all cases, this two-step transformation of 𝒯(x) into 𝒯′′ is a terminal operation.

If, however, neither x nor its children is full, the rotation or demotion we performed in case 6, 7 or 8 did not create any full node. Indeed, replacing 𝒯(x) by a tree of smaller rank did not make any ancestor of x a full node. The only other nodes whose rank or induced sub-tree was changed are x and its children, which we precisely assumed are not full either. In that case, our operation is transient if the parent of x, say y, was a {1,2}-node, and became a four-node.

Lemma 4.

Each transient operation triggered by a deletion decreases the number of {1,2}-nodes, and does not increase the number of full nodes, also decreasing the tree potential.

Proof.

We just proved that transient operations are those where y was a {1,2}-node and neither its new child x nor the children of x became full. Hence, we did not create any full node, and it suffices to check, on a case-by-case basis, that the number of {1,2}-nodes decreased:

  1. i)

    in case 6, we destroyed two {1,2}-nodes (𝖡=x1 and y);

  2. ii)

    in case 7, we destroyed at least two {1,2}-nodes (𝖡=x1 and y) to create at most one {1,2}-node (𝖡=x1 or 𝖥=x2, in case 𝖣 used to be a {1,2}-node);

  3. iii)

    in case 8, we destroyed one {1,2}-node (y).

4 Top-down updates

4.1 Overview and complexity analysis

Our top-down algorithms for inserting a key k in a tall enough AVL tree 𝒯 and for deleting k from 𝒯 follow the same general ideas, which were already successfully applied to weak AVL trees [5]. We will discover, in a top-down manner, the branch x0,x1, below which k should be inserted or deleted, where x0 is the root of 𝒯 and each node xi+1 is a child of the previous node xi. Using a 𝒪(1) look-ahead, and after discovering the first 𝒪(1) nodes on that branch, we must already decide what to do.

If we are lucky enough, we found a node xi, for some integer i1, that is deemed safe: propagating a zero-edge (during insertions) or four-node (during deletions) that we would have planted along our branch far enough below xi will stop when meeting xi. When such a node xi exists among the top few nodes of the branch, we say that 𝒯 was friendly; indeed, we should simply focus on updating the tree 𝒯(xi), whose rank is smaller than 𝒯 itself, and which will be transformed into a new tree of the same rank.

This fortunate case suggests focusing only on updating trees whose root is safe. Hence, if 𝒯 has an unsafe root, a preliminary step will consist in transforming 𝒯 into a tree whose root is safe, which we will then update; all subsequent (recursively called) updates will be performed on trees whose root is safe.

If, however, 𝒯 has a safe root but is unfriendly, we will choose a deep enough node, say x, and transform the sub-tree 𝒯(x) into a tree 𝒯 whose root is safe, while promoting it (during insertions) or demoting it (during deletions). This transformation will create a zero-edge or a four-node that will be propagated upward and whose propagation will stop when meeting the safe root x; afterward, it just remains to update 𝒯.

Once implemented, these ideas lead to a bottom-up updating algorithm, whose amortised complexity we study now.

Theorem 5.

The updating algorithm whose skeleton we just presented, and whose details are presented in Sections 4.2 (for insertions) and 4.3 (for deletions) performs 𝒪(1) amortised write operations per update.

Proof.

Like for Theorem 1, we start from an empty tree, perform n insertion or deletion updates, and keep track of the variations of tree potential while these updates are performed. There is a large enough constant 𝖪 such that each update requires:

  1. 1.

    optionally, making the tree root safe, which increases the tree potential by at most 𝖪;

  2. 2.

    identifying some safe nodes; this requires no write operation and does not change the tree potential;

  3. 3.

    operating on trees small height, i.e., whose height is smaller than a constant hmin; this requires performing a constant number of write operations, and increases the tree potential by a at most 𝖪;

  4. 4.

    performing a certain number of batches, each of which consists in (i) cut-demoting a node to make it safe and make its ancestor a four-node; (ii) optionally, performing a cut rebalancing for ensuring that some node will stop propagating four-nodes; (iii) 𝒪(1) transient operations; and (iv) one “terminal” operation where the four-node stop being propagated.

In each batch, steps (i), (ii) and (iv) increase the tree potential by no more than a constant, and each transient operation decreases the tree potential. Thus, each batch decreases the tree potential by 𝒪(1), i.e., by no more than 𝖫, where 𝖫 is a constant; it also requires performing 𝒪(+1) write operations. In particular, if 𝖫+1, and if 𝖳 batches of operations were performed in total, the potential increased by at most n𝖪𝖳; of course, making sure that 𝖫+1 means that we will need to determine a suitable constant 𝖫.

The tree potential started with the value 0, and ended with a non-negative value, which shows that 𝖳2n𝖪. Therefore, in total, these n updates required performing no more than 𝒪(n)+𝖳×𝒪(𝖫+1)𝒪(n) write operations.

Hence, it remains to:

  1. 1.

    define our notions of safe nodes, both for insertions and for deletions;

  2. 2.

    prove that trees rooted at an unsafe node can be cut promoted to make their root safe, during insertions;

  3. 3.

    prove that trees rooted at an unsafe node can be cut demoted to make their root safe, during deletions;

  4. 4.

    compute the constants hidden in the 𝒪(1) notations above, in order to effectively compute a constant for which batches decrease the tree potential.

Below, we will prove that choosing =48 during insertions, and =126 during deletions is enough for our purposes: each batch will decrease the tree potential by at least 1, and other operations will increase the tree potential by 𝒪() per update. These values of are grossly sub-optimal: for example, choosing =15 during insertions, and =20 during deletions would be enough; computing these better values, although not intrinsically difficult, involves lengthy case-by-case studies that we chose to avoid here.

4.2 Insertion

Below, we say that a node x is insertion-safe (or simply safe, when the context is clear) when (i) 𝗋(x)=1 and x is a {1,2}-node, or (ii) 𝗋(x)2 and x is not full. We showed in Section 3.2 that each tree rooted at a full node, i.e., each tree 𝒯 of rank 𝗋2 whose root is unsafe, can be subject to a cut promotion, resulting in a tree 𝒯 of rank 𝗋+1 whose root is a {1,2}-node, thus being safe.

This makes following the recipe of Section 4.1 easy once the desired integer is chosen. First, if 𝗋(𝒯)+2, just use the bottom-up algorithm of Section 3.3, which may result in a tree 𝒯 of rank 𝗋(𝒯)=𝗋(𝒯)+1 only in case 𝒯 was rooted at an unsafe node. Otherwise, 𝗋(𝒯)+3, and:

  1. 1.

    if 𝒯 is rooted at an unsafe node, cut-promote it, thus obtaining a tree 𝒯 of rank 𝗋(𝒯)+4 whose root is safe;

  2. 2.

    if 𝒯 is friendly, let i be the least positive integer for which xi is safe: we simply focus on inserting the key k in 𝒯(xi);

  3. 3.

    if 𝒯 is unfriendly, the first nodes x1, on our branch are all unsafe; thus, they are {1,1}-nodes, and the branch contains at least nodes, with the guarantee that 𝗋(x)𝗋(𝒯)(+1)2, so that all nodes x1,,x are full; hence we cut-promote 𝒯(x) and promote all nodes x1,,x1, thereby creating a zero-edge between x0 and x1, which we eliminate by using the cut rebalancing prescribed in Section 3.3.

Lemma 6.

Step 3 consists in a batch operation that decreases the tree potential by 47 or more.

Proof.

It suffices to decompose step 3 in atomic operations:

  1. i)

    a cut promotion, which increases the tree potential by 14 or less;

  2. ii)

    replacing each of the 1 full nodes x1,,x1 by a {1,2}-node, which decreases the tree potential by 1;

  3. iii)

    eliminating a zero-edge through a terminal cut rebalancing performed on a cut with 2 to 4 nodes, and optionally followed by a cut demotion; this increases the tree potential by 32 or less.

4.3 Deletion

We will now characterise deletion-safe (or simply safe, when the context is clear) nodes: a node x should be safe when they can stop propagating four-nodes. There will be three families of safe nodes: (i) surely safe nodes, which stop propagating four-nodes even when using the textbook deletion algorithm, (ii) barely safe nodes, which are some of the nodes that can be transformed to create surely safe nodes in their close vicinity, and (iii) simply safe nodes, which stop propagating four-nodes when using the algorithm of Section 3.4.

Surely safe nodes are the nodes x of rank 𝗋(x)1 with profile (1,1), or rank 𝗋(x)2 and profile (2,2,2). Indeed, if some child xi of x is to be replaced by a node of smaller rank, either (i) x had profile (1,1), in which case its new profile is simply {1,2}, or (ii) x had profiles {1,2} and (2,2,2), in which case xi was the 2-child of x (because the 1-child of x had profile (1,1)), and we fall in case 5 of the bottom-up algorithm.

Detecting other kinds of safe nodes is more challenging; in particular, whether a node x is safe may depend on which descendants of x are to be replaced by nodes of smaller rank. More precisely, let x=x be a node that belongs to the branch x0,x1, below which the key k will be deleted, and let z=x+4.

Lemma 7.

Each tree 𝒯 can be subject to a cut rebalancing, called cut saving, that yields a tree 𝒯 of rank 𝗋(𝒯) or 𝗋(𝒯)1 in which z has a surely safe ancestor. This cut saving increases the tree potential by no more than 38.

Proof.

Let x be the root of 𝒯. If x is surely safe already, there is nothing to do. Hence, we assume that x has profiles {1,2} and {2,2,3}. Replacing its descendants of rank 𝗋(x)2 by their children gives a cut (𝖠,𝖢,𝖤,𝖦,𝖨) with profile {3,3,3,3,3}, {3,3,3,3,4} or {3,3,3,4,4}.

In the first case, the children of x must have profiles (1,1) and (2,2,2), and once again, there is nothing to do. In the third case, cut-demoting 𝒯(x) transforms it into a tree 𝒯 of rank 𝗋(x)1 whose root is a {1,1}-node.

It remains to treat the second case. Let y be the node of rank 𝗋(x)4 in the cut (𝖠,𝖢,𝖤,𝖦,𝖨), and let 𝖡, 𝖣, 𝖥 and 𝖧 be the cut ancestors. By symmetry, we assume that y coincides with 𝖠, 𝖢 or 𝖤; if y=𝖤, that z descends from 𝖤, 𝖦 or 𝖨; and if y=𝖤 and z descends from 𝖤, there are no more {1,2}-nodes among 𝖠 and 𝖢 than among 𝖦 and 𝖨. Then, we distinguish ten cases; the tree 𝒯 will be of rank 𝗋(x) in cases 1 to 8, and 𝗋(x)1 in cases 9 and 10:

Figure 4: Trees the saving procedure may result in, or use as intermediate step (in cases 9 and 10).
  1. 1.

    If (i) y=𝖠 or y=𝖢 and (iii) z descends from 𝖤, 𝖦 or 𝖨, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯1); this makes 𝖥 a surely safe ancestor of z.

  2. 2.

    If (i) y=𝖠, (ii) 𝖢 is a {1,1}-node and (iii) z descends from 𝖠 or 𝖢, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯1); this makes 𝖡 a surely safe ancestor of z.

  3. 3.

    If (i) y=𝖠, (ii) 𝖤 is a {1,1}-node (whereas 𝖢 is not), and (iii) z descends from 𝖠 or 𝖢, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯2); this makes 𝖢 a surely safe ancestor of z.

  4. 4.

    If (i) y=𝖢, (ii) 𝖠 is a {1,1}-node, and (iii) z descends from 𝖠 or 𝖢, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯1); this makes 𝖡 a surely safe ancestor of z.

  5. 5.

    If (i) y=𝖢, (ii) 𝖤 is a {1,1}-node (whereas 𝖠 is not), and (iii) z descends from 𝖠 or 𝖢, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯4); this makes 𝖡 a surely safe ancestor of z.

  6. 6.

    If (i) y=𝖤 and (iii) z descends from 𝖦 or 𝖨, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯5); this makes 𝖧 a surely safe ancestor of z.

  7. 7.

    If (i) y=𝖤, (ii) 𝖢 is a {1,1}-node, and (iii) z descends from 𝖤, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯5); this makes 𝖣 a surely safe ancestor of z.

  8. 8.

    If (i) y=𝖤, (ii) 𝖠 is a {1,1}-node (whereas 𝖢 is not), and (iii) z descends from 𝖤, we rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯6); this makes 𝖥 a surely safe ancestor of z.

  9. 9.

    If (i) y=𝖠 or y=𝖢, (ii) all of 𝖠, 𝖢 and 𝖤 coincide with y or are {1,2}-nodes, and (iii) z descends from 𝖠 or 𝖢, we first rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯3). The sub-tree 𝒯3(𝖣) has profile {3,3,3,4,4} and we cut-demote it, before demoting the node 𝖥 itself; this yields a tree of rank 𝗋(x)-1 whose root 𝖥 is a {1,1}-node.

  10. 10.

    If (i) y=𝖤, (ii) all of 𝖠, 𝖢, 𝖦 and 𝖨 are {1,2}-nodes, and (iii) z descends from 𝖤, we first rebalance 𝒯(x) as shown in Figure 4 (tree 𝒯5). The sub-tree 𝒯5(𝖡) has profile {3,3,3,4,4} and we cut-demote it, before demoting the node 𝖥 itself; this yields a tree of rank 𝗋(x)-1 whose root 𝖥 is a {1,1}-node.

All cases consist in performing a cut rebalancing operation on a cut with at most 7 nodes, optionally followed by a cut demotion. Hence, the saving operation increases the tree potential by no more than 38.

We can now present two additional notions of safety. First, a node x is said to be barely safe when the cut saving transforms 𝒯(x) into a tree 𝒯(x) of rank 𝗋(x). Second, as already mentioned, a node is said to be simply safe if its stops propagating four-nodes. Fortunately, deciding whether a node is simply safe is easy, as outlined by the following result.

Lemma 8.

Being simply safe depends on local criteria only.

Proof.

Provided that the node x=x is not surely safe, and if the node t=x+1 has just been replaced by a node of smaller rank, the textbook algorithm must now replace 𝒯(x) by a tree 𝒯 whose root x is a {1,1}-node with rank 𝗋(x)=𝗋(x)1. Furthermore, as can be seen in Figure 2, at least one of its children x1 and x2 will also be a {1,1}-node. Hence, we say that x is m-strong when the child xm is a {1,1}-node.

Being 1- or 2-strong depends on local criteria only. Indeed, the node x is 1-strong when:

  • t coincides with x1, and x has profile (1,2), (2,3,2), (2,3,3,3) or (2,3,4,3); or

  • t coincides with x2, and x has profile (3,3,1), (3,3,3,2) or (3,3,4,2).

Hence, being 1-strong is a purely local criterion and, symmetrically, so is being 2-strong.

Then, knowing whether t is 1-strong and/or 2-strong allows checking whether x itself is safe, based on other local criteria. For instance, when t coincides with x2, the node x is safe when it has profile (1,1) or (2,2,2), or in the following cases:

  1. 6.

    𝗋(x11)=𝗋(x2)=𝗋2 and 𝗋(x12)=𝗋3, and

    1. a)

      x has profile (3,4,4,3,2), (3,3,4,4,2) or (2,4,5,5,2), or

    2. b)

      x has profile (2,4,4,2) and x2 is 1-strong;

  2. 7.

    𝗋(x11)=𝗋3 and 𝗋(x12)=𝗋(x2)=𝗋2, and

    1. a)

      x has profile (3,4,4,3,2), (3,3,4,4,2), (4,5,5,4,4,3,2), (4,5,5,4,4,4,2), . (4,4,5,5,4,3,2), (4,4,5,5,4,4,2), (3,3,4,5,5,2) or (3,4,4,5,5,2), or

    2. b)

      x has profile (3,3,4,4,2) or (3,4,4,4,2) and x2 is 1-strong;

  3. 8.

    𝗋(x1)=𝗋2 and 𝗋(x2)=𝗋1, and

    1. a)

      x has profile (3,4,4,1), or

    2. b)

      x has profile (3,3,1) and x2 is 1-strong.

These cases were listed by simply enumerating all conditions that could leaf to creating a full node while cut-rebalancing a tree in cases 6 to 8. Hence, some of them are redundant; for instance, in case 7a, if x has profile (3,3,4,5,5,2), it must also have had profile (3,3,4,4,2), which was sufficient for flagging it as safe. Symmetric characterisations exist when t coincides with the left child x1 of the node x.

Safe nodes and the above saving procedure allow us to implement the recipe succinctly described in Section 4.1. If 𝒯 is of rank 𝗋(𝒯)2+4 and its root is barely safe, first use the saving procedure to let a surely safe node appear on the branch below which k will be deleted, and then use the bottom-up algorithm of Section 3.4; this will result in a tree 𝒯 of rank 𝗋(𝒯)=𝗋(𝒯). Otherwise, if 𝒯 is of rank 𝗋(𝒯)2+4 and its root is not barely safe, directly use the bottom-up algorithm of Section 3.4. It may result in a tree 𝒯 of rank 𝗋(𝒯)=𝗋(𝒯)1 only when 𝒯 was rooted at an unsafe node.

We focus now on trees 𝒯 of rank 𝗋(𝒯)2+5:

  1. 1.

    If 𝒯 is rooted at an unsafe node, the saving procedure transforms it into a tree 𝒯 of rank 𝗋(𝒯)2+4 whose root is safe.

  2. 2.

    If 𝒯 is friendly, let i be the least positive integer for which xi is safe: we simply focus on inserting the key k in 𝒯(xi).

  3. 3.

    If 𝒯 is unfriendly, we first consider two sub-cases: if the root x0 of 𝒯 is surely or simply safe, we set α=x0; otherwise, applying the saving algorithm to 𝒯 results, without altering the sub-tree 𝒯(x4), in a tree 𝒯 of rank 𝗋(𝒯) in which x4 has a surely safe ancestor, the least of which we call α. Then, observing that 𝗋(x)𝗋(x)24, we apply the saving procedure to the sub-tree 𝒯(x), which makes x1 a four-node. We propagate once this four-node by using the textbook deletion algorithm, i.e., we do not cut-promote full-nodes that might result from using the textbook algorithm; then, we keep propagating our four-node using the algorithm from Section 3.4, a propagation that will stop only when reaching the node α, at which point the four-node is eliminated.

Lemma 9.

Step 3 consists in a batch operation that decreases the tree potential by 125 or more.

Proof.

Like for Lemma 4, it suffices to decompose step 3 in atomic operations:

  1. i)

    optionally, using the saving procedure (cases 1 to 8) on 𝒯, which increases the tree potential by no more than 38;

  2. ii)

    cut-demoting 𝒯(x) or using the saving procedure (case 9 or 10) on 𝒯(x), which increases the tree potential by no more than 38;

  3. iii)

    propagating once the four-node by using the textbook deletion algorithm on 𝒯(x1): this consists in rebalancing a cut with at most 4 nodes, thus increasing the tree potential by no more than 12;

  4. iv)

    propagating the four-node by using the algorithm of Section 3.4 on nodes x2,,x4, and stopping just before one meets the surely safe ancestor α of x4; there are at least 5 such propagations, hence the tree potential decreases by at least 5;

  5. v)

    eliminating the four-node by using the algorithm of Section 3.4 on α; this consists in rebalancing a cut with at most 4 nodes, then possibly performing a cut promotion, and even a node promotion afterward; these three phases, two of which are optional, increase the tree potential by at most 12, 14, and 6, respectively.

References

  • [1] Georgii Adel’son-Velskii and Evgenii Landis. An algorithm for organization of information. In Doklady Akademii Nauk, volume 146(2), pages 263–266. Russian Academy of Sciences, 1962.
  • [2] Mahdi Amani, Kevin Lai, and Robert Tarjan. Amortized rotation cost in AVL trees. Information Processing Letters, 116(5):327–330, 2016. doi:10.1016/J.IPL.2015.12.009.
  • [3] Norbert Blum and Kurt Mehlhorn. On the average number of rebalancing operations in weight-balanced trees. Theoretical Computer Science, 11:303–320, 1980. doi:10.1016/0304-3975(80)90018-3.
  • [4] Leonidas Guibas and Robert Sedgewick. A dichromatic framework for balanced trees. In 19th Annual Symposium on Foundations of Computer Science (FOCS), pages 8–21. IEEE Computer Society, 1978. doi:10.1109/SFCS.1978.3.
  • [5] Bernhard Haeupler, Siddhartha Sen, and Robert E Tarjan. Rank-balanced trees. ACM Transactions on Algorithms (TALG), 11(4):1–26, 2015. doi:10.1145/2689412.
  • [6] Thomas Hibbard. Some combinatorial properties of certain trees with applications to searching and sorting. Journal of the ACM, 9(1):13–28, 1962. doi:10.1145/321105.321108.
  • [7] Vincent Jugé. Grand-children weight-balanced binary search trees. In 19th International Symposium on Algorithms and Data Structures (WADS), volume 349 of LIPIcs, pages 14:1–14:20, 2025. doi:10.4230/LIPICS.WADS.2025.14.
  • [8] Tony Lai and Derick Wood. A top-down updating algorithm for weight-balanced trees. International Journal of Foundations of Computer Science, 4(4):309–324, 1993. doi:10.1142/S0129054193000201.
  • [9] Jürg Nievergelt and Edward Reingold. Binary search trees of bounded balance. In 4th Annual ACM Symposium on Theory of Computing (STOC), pages 137–142. ACM, 1972. doi:10.1145/800152.804906.
  • [10] Hendrik Olivié. A study of balanced binary trees and balanced one-two trees. Universitaire Instelling Antwerpen (Belgium), 1980.
  • [11] Hendrik Olivié. A new class of balanced search trees: half-balanced binary search tress. RAIRO. Informatique théorique, 16(1):51–71, 1982. doi:10.1051/ita/1982160100511.
  • [12] Daniel Sleator and Robert Tarjan. Self-adjusting binary search trees. Journal of the ACM, 32(3):652–686, 1985. doi:10.1145/3828.3835.