0

I am working on an AVL tree such that each Node holds the deepest sub-tree beneath it. Here is an example of the tree, where the representation is value: height.

0: 2
 \
  1: 1
   \
    2: 0

To check the balance, I used the following function given an AVL Tree node or undefined. Following code snippet is written in TypeScript. The formula used is left - right depth, with undefined child nodes returning -1.

private balanceFactor(currentNode: AVLTreeNode<E> | undefined): number {
    if (currentNode === undefined) {
      return -1;
    }
    let leftHeight = currentNode.hasLeft() ? currentNode.getLeft().getHeight() : -1;
    let rightHeight = currentNode.getRight() ? currentNode.getRight().getHeight() : -1;
    return leftHeight - rightHeight
}

To implement the logic code of the rotations, I found a previous stack post: https://stackoverflow.com/a/19281663/10448256

The post deals the following logic for the double rotations:

balance(N) = Depth(Nleft) - Depth(Nright)

if (balance(P) > 1) {
    if (balance(L) < 0) {
        rotate_left(L);
    }
    rotate_right(P);
}
else if (balance(P) < -1) {
    if (balance(R) > 0) {
        rotate_right(R); 
    }
    rotate_left(P);      
}

Consider the following double rotation scenario:


    2: 2
   /
  1: 1
   \
    0: 0

When the balance factor is called on node 2, it creates a BF of two (2 - 0), and since the right is undefined, the balance factor of the right is -1. This results in a double rotation, correctly creating the following tree:

         1: 1
      /       \
    0: 0     2: 0

While the double rotation works, it does not allow for single rotations. If the first example is tried again:

2: 2
 \
  1: 1
   \
    0: 0

It creates a balance factor of 2 at value 2, where the left node is a -1 because it is undefined, incorrectly resulting in a double rotation detection.

What logic should I use to identify the difference between double and single rotations?

M. Nicol
  • 73
  • 1
  • 5
  • 1
    Note that a proper AVL implementation uses balance factor as a property, and keeps it updated so that there is no need to calculate depth/height of anything. – trincot May 27 '23 at 16:44
  • 1
    The logic to identify which rotation to apply is fully explained at [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree). – trincot May 27 '23 at 16:47
  • @trincot When a node is added to the tree, I decrease the balance factor of each node above it by one if it is on the left and increase by one if it is on the right? This will allow to only keep track of -1, 0 and 1 instead of the subtree height? – M. Nicol May 27 '23 at 22:24
  • *"I decrease the balance factor of each node above it by one if it is on the left and increase by one if it is on the right?"*: no, it isn't that simple. You can imagine adding a node where the balance factor of the parent changes to 0 and no further ancestors get a change in their balance factor. Again, all this is explained at Wikipedia. – trincot May 28 '23 at 06:56
  • @trincot Wikipedia does rotate left, rotate right, rotate left-right, and rotate right-left as four separate functions. Is there any way to rotate right and left in two separate function calls? The balance factor code starts to get cryptic. – M. Nicol May 29 '23 at 03:47
  • Also, Wikipedia does not have the pseudo-code for rotation detection, and Figure 3 of the double rotation has a error. – M. Nicol May 29 '23 at 03:59
  • The Wikipedia article is quite clear. For insertion it says *"There are four possible variants of the violation: ...And the rebalancing is performed differently:"*. This has also been implemented many times in Q&A on this site. Like [here](https://stackoverflow.com/a/70187428/5459839) and [here](https://stackoverflow.com/a/72846580/5459839). – trincot May 29 '23 at 07:20
  • I have posted an answer 3 days ago. Did you check it out? Any feed-back? – trincot Jun 01 '23 at 19:07

1 Answers1

0

The logic that is implemented in the code you found in a related question is correct.

While the double rotation works, it does not allow for single rotations.

The inner if condition in the referenced code determines whether a double or single rotation should occur.

If the first example is tried again:

2: 2
 \
  1: 1
   \
    0: 0

It creates a balance factor of 2 at value 2, where the left node is a -1 because it is undefined, incorrectly resulting in a double rotation detection.

This is due to an error in your balanceFactor function. When a node is absent, you have there an empty tree. Such a tree is balanced. There is no good reason to return -1 in that case as if that empty tree is out of balance. You should return 0:

private balanceFactor(currentNode: AVLTreeNode<E> | undefined): number {
    if (currentNode === undefined) {
      return 0; // Correction
    }
    let leftHeight = currentNode.hasLeft() ? currentNode.getLeft().getHeight() : -1;
    let rightHeight = currentNode.getRight() ? currentNode.getRight().getHeight() : -1;
    return leftHeight - rightHeight
}

Remark: it is not efficient to call getHeight in the process of determining the balance factor. This can be done incrementally without the need to know a node's height. See for instance this implementation which uses a lookup table (matrix) for the balance factors found in the two nodes that participate in a rotation, giving the new balance factors for those nodes.

trincot
  • 317,000
  • 35
  • 244
  • 286