5

I am looking into AVL trees and can not seem to find a reference code about removal (either by Googling or from a couple of textbooks I have handy).
I am not sure why is this, but do you know of any reference/example of deletion of AVL in java?
(I only found this:avl tree removal which it states in the link that it failed under testing)

Cratylus
  • 52,998
  • 69
  • 209
  • 339

3 Answers3

3

I have an implementation of an AVL Tree in Java which has been well tested, if you'd like to use it for reference. It is based on the wikipedia description and it is commented pretty well.

Just like when you have to balance after a regular BST insert. You remove the node like a BST and then balance according to the below algorithm.

The cases for balancing after a BST remove are (node is the parent of the node which was used to replace the removed node):

    ... remove code ...
    // Re-balance the tree all the way up the tree
    while (nodeToRefactor != null) {
        nodeToRefactor.updateHeight();
        balanceAfterDelete(nodeToRefactor);
        nodeToRefactor = (AVLNode<T>) nodeToRefactor.parent;
    }
    ... remove code ...

    ... balance code ...
    int balanceFactor = node.getBalanceFactor();
    if (balanceFactor==-2 || balanceFactor==2) {
        if (balanceFactor==-2) {
            AVLNode<T> ll = (AVLNode<T>) node.lesser.lesser;
            int lesser = ll.height;
            AVLNode<T> lr = (AVLNode<T>) node.lesser.greater;
            int greater = lr.height;
            if (lesser>=greater) {
                rightRotation(node);
            } else {
                leftRotation(node.lesser);
                rightRotation(node);
            }
        } else if (balanceFactor==2) {
            AVLNode<T> rr = (AVLNode<T>) node.greater.greater;
            int greater = rr.height;
            AVLNode<T> rl = (AVLNode<T>) node.greater.lesser;
            int lesser = rl.height;
            if (greater>=lesser) {
                leftRotation(node);
            } else {
                rightRotation(node.greater);
                leftRotation(node);
            }
        }
    }
Justin
  • 4,196
  • 4
  • 24
  • 48
  • +1 for the reference.I see that you use parent links.Are they necessary.So far I have seen insert implementations for AVL that do not need a parent link.Is it required for deletion? – Cratylus May 27 '12 at 14:01
  • Parent references certainly aren't necessary, if you are trying to conserve space per node. But they generally make the implementation cleaner and simpler in my opinion. – Justin May 27 '12 at 14:11
  • FYI, For a 100,000 Integer (big I integer) AVL Tree this implementation uses 4MB. I've got all the stats for the AVL Tree on the main page. http://code.google.com/p/java-algorithms-implementation/ – Justin May 27 '12 at 14:13
  • No I am not wondering about space.I was wondering if perhaps the algorithm actually required the use of parent links. – Cratylus May 27 '12 at 14:21
  • An AVL Tree does not require a parent link but my implementation does in the updateHeight() method and I also use the parent link to balance up the tree on add()/remove(). – Justin May 27 '12 at 14:34
  • I am not sure on this, but the code posted looks convoluted to me.If I understand your answer correctly, what you describe is a regular `delete` operation as done on a BST and then what is left is to update the balance. If I understand this correctly, only a `single rotation` is applicable after a `delete`. Never a `double rotation` with a child in order to rebalance. Do you agree? Am I correct on this? – Cratylus Jun 01 '12 at 17:46
  • I'm not sure what in particular looks convoluted in the code but I can try to answer the question. I agree that it may only need a single rotation since the height could never decrease by more than 1 which would require a single rotation to correct. – Justin Jun 01 '12 at 18:10
  • Isn't an AVL just a BST?If we just modify a `BinaryNode` to add `height` as a member and recursively traverse the tree to insert or delete, won't the code be much simpler.It would basically a slight modification of BST – Cratylus Jun 01 '12 at 19:51
  • Essentially, AVL is a self-balancing BST. But the reason why I didn't just call the BST remove code is because you have to selectively update node heights when you delete since you don't want to just recurse down/up the whole tree on a height change. – Justin Jun 01 '12 at 20:00
  • @user384706 Needless to say, I am still looking for a good way to encapsulate the BST specific code from the AVL code. I just haven't found a good way to do it on remove. – Justin Jun 01 '12 at 22:42
  • If you are still looking into this but I've recently updated the balancing code after delete. After you brought up the point, I found that my code wasn't correct. So, I've correct it using this as my template. http://www.blackbam.at/blackbams-blog/2012/05/04/avl-tree-implementation-in-java/ There are only 2 case (with 2 sub-cases). I'll update my answer above to be correct. – Justin Jun 04 '12 at 19:04
  • @user384706 I finally figure out how to encapsulate the BST code from the AVL code. I've also updated the delete balancing code like my last comment. Hope it helps. – Justin Jun 05 '12 at 19:16
  • @user384706 Did you find the update useful? Did it answer your question(s)? – Justin Jun 12 '12 at 22:46
  • Knuth says that deletion may require up to `lg(N)` rebalances, hence some sort of loop or recursion is necessary following a delete. The poster's reference has such a loop, but the code quoted here does not. – Richard Dec 11 '12 at 12:07
  • @Richard I am only showing the balacing code. It is wrapped in a loop which works it's way up the tree. I've added the loop code. – Justin Dec 11 '12 at 14:59
2

The algorithm isn't that bad, once you have an implementation of balance()...

The first implementation that comes to mind is the implementation of TreeList in Apache Commons Collections, which is a list backed by an AVL tree. http://www.docjar.org/html/api/org/apache/commons/collections/list/TreeList.java.html has the source code.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • `The algorithm isn't that bad` What do you mean here?That the approach to remove from an AVL tree is too convoluted in general? – Cratylus May 27 '12 at 09:57
  • That it _isn't_ very convoluted, is what I'm saying. (As far as such things go.) Once you understand the general structure of the recursion that's going on -- which is demonstrated in the linked code -- the rest isn't too bad. – Louis Wasserman May 27 '12 at 10:01
  • I will look into this thank you.From a quick skim the `AVLNode` class is a lot different from what I have read so far. I am not sure if a lot more extra data are needed to actually implement the removal or this ALV tree implementation is just more "optimal" than what I have seen in textbooks – Cratylus May 27 '12 at 10:07
  • Note that a fair amount of the code here is needed because this is implementing a _list_, not a map or a set. Not all of these are needed in an everyday AVL tree. – Louis Wasserman May 27 '12 at 10:37
2

Tree deletion works by searching (in the same manner as lookup) until it finds the node to be removed, replaces it with its minimal successor (you could also use its maximal predecessor), then rebalances the tree. The rebalancing is done from the bottom up; after finding the node to be removed, the algorithm plunges down the left spine of the right subtree, finds the minimal successor, and rebalances as it works its way back up the tree to the node being deleted, which is replaced by the minimal successor. The only special case occurs when the item being deleted is not present in the tree, in which case the tree is returned unchanged. Here is my implementation of AVL trees, in Scheme; by using recursion rather than the more traditional iteration, the code becomes very simple:

(define (tree k v l r)
  (vector k v l r (+ (max (ht l) (ht r)) 1)))
(define (key t) (vector-ref t 0))
(define (val t) (vector-ref t 1))
(define (lkid t) (vector-ref t 2))
(define (rkid t) (vector-ref t 3))
(define (ht t) (vector-ref t 4))
(define (bal t) (- (ht (lkid t)) (ht (rkid t))))
(define nil (vector 'nil 'nil 'nil 'nil 0))
(vector-set! nil 2 nil)
(vector-set! nil 3 nil)
(define (nil? t) (eq? t nil))

(define (rot-left t)
  (if (nil? t) t
    (tree (key (rkid t))
          (val (rkid t))
          (tree (key t) (val t) (lkid t) (lkid (rkid t)))
          (rkid (rkid t)))))

(define (rot-right t)
  (if (nil? t) t
    (tree (key (lkid t))
          (val (lkid t))
          (lkid (lkid t))
          (tree (key t) (val t) (rkid (lkid t)) (rkid t)))))

(define (balance t)
  (let ((b (bal t)))
    (cond ((< (abs b) 2) t)
          ((positive? b)
            (if (< -1 (bal (lkid t))) (rot-right t)
              (rot-right (tree (key t) (val t)
                (rot-left (lkid t)) (rkid t)))))
          ((negative? b)
            (if (< (bal (rkid t)) 1) (rot-left t)
              (rot-left (tree (key t) (val t)
                (lkid t) (rot-right (rkid t)))))))))

(define (lookup lt? t k)
  (cond ((nil? t) #f)
        ((lt? k (key t)) (lookup lt? (lkid t) k))
        ((lt? (key t) k) (lookup lt? (rkid t) k))
        (else (cons k (val t)))))

(define (insert lt? t k v)
  (cond ((nil? t) (tree k v nil nil))
        ((lt? k (key t))
          (balance (tree (key t) (val t)
            (insert lt? (lkid t) k v) (rkid t))))
        ((lt? (key t) k)
          (balance (tree (key t) (val t)
            (lkid t) (insert lt? (rkid t) k v))))
        (else (tree k v (lkid t) (rkid t)))))

(define (delete-successor t)
  (if (nil? (lkid t)) (values (rkid t) (key t) (val t))
    (call-with-values
      (lambda () (delete-successor (lkid t)))
      (lambda (l k v)
        (values (balance (tree (key t) (val t) l (rkid t))) k v)))))

(define (delete lt? t k)
  (cond ((nil? t) nil)
        ((lt? k (key t))
          (balance (tree (key t) (val t)
            (delete lt? (lkid t) k) (rkid t))))
        ((lt? (key t) k)
          (balance (tree (key t) (val t)
            (lkid t) (delete lt? (rkid t) k))))
        ((nil? (lkid t)) (rkid t))
        ((nil? (rkid t)) (lkid t))
        (else (call-with-values
                (lambda () (delete-successor (rkid t)))
                (lambda (r k v) (balance (tree k v (lkid t) r)))))))
user448810
  • 17,381
  • 4
  • 34
  • 59