I'm looking for the best way to calculate a nodes balance in an AVL-tree. I thought I had it working, but after some heavy inserting/updating I can see that it's not working correct (at all).
This is kind of a two-part question, the first part would be how to calculate the height of a sub-tree, I know the definition "The height of a node is the length of the longest downward path to a leaf from that node." and I understand it, but I fail at implementing it. And to confuse me further this quote can be found on wikipedia on tree-heights "Conventionally, the value -1 corresponds to a subtree with no nodes, whereas zero corresponds to a subtree with one node."
And the second part is getting the balance factor of a sub-tree in an AVL tree, I've got no problem understanding the concept, "get the height of your L
and R
sub-trees and subtract R
from L
". And this is defined as something like this: BALANCE = NODE[L][HEIGHT] - NODE[R][HEIGT]
Reading on wikipedia says this on the first few lines describing insertions into an AVL tree: "If the balance factor becomes -1, 0, or 1 then the tree is still in AVL form, and no rotations are necessary."
It then goes on, saying this "If the balance factor becomes 2 or -2 then the tree rooted at this node is unbalanced, and a tree rotation is needed. At most a single or double rotation will be needed to balance the tree." - which I have no trouble grasping.
But (yes, there's always a but).
Here's where it gets confusing, the text states "If the balance factor of R is 1, it means the insertion occurred on the (external) right side of that node and a left rotation is needed". But from m understanding the text said (as I quoted) that if the balance factor was within [-1, 1]
then there was no need for balancing?
I feel I'm so close to grasping the concept, I've gotten the tree rotations down, implemented a normal binary search tree, and on the brink of grasping AVL-trees but just seem to be missing that essential epiphany.
Edit: Code examples are preferred over academic formulas as I've always had an easier time grasping something in code, but any help is greatly appreciated.
Edit: I wish I could mark all answers as "accepted", but for me NIck's answer was the first that made me go "aha".
Give
BinaryTree<T, Comparator>::Node
asubtreeHeight
data member, initialized to 0 in its constructor, and update automatically every time with:Note that data members
subtreeSize
anddepthFromRoot
are also updated. These functions are called when inserting a node (all tested), e.g.If removing a node, use a different version of
removeLeft
andremoveRight
by replacingsubtreeSize++;
withsubtreeSize--;
. Algorithms forrotateLeft
androtateRight
can be adapted without much problem either. The following was tested and passed:where
Here is the entire code: http://ideone.com/d6arrv
Here's an alternate way of finding height. Add an additional attribute to your node called height:
Now, we'll do a simple breadth-first traversal of the tree, and keep updating the height value for each node:
Cheers,
Part 1 - height
As starblue says, height is just recursive. In pseudo-code:
Now height could be defined in two ways. It could be the number of nodes in the path from the root to that node, or it could be the number of links. According to the page you referenced, the most common definition is for the number of links. In which case the complete pseudo code would be:
If you wanted the number of nodes the code would be:
Either way, the rebalancing algorithm I think should work the same.
However, your tree will be much more efficient (O(ln(n))) if you store and update height information in the tree, rather than calculating it each time. (O(n))
Part 2 - balancing
When it says "If the balance factor of R is 1", it is talking about the balance factor of the right branch, when the balance factor at the top is 2. It is telling you how to choose whether to do a single rotation or a double rotation. In (python like) Pseudo-code:
I hope this makes sense
Height is easily implemented by recursion, take the maximum of the height of the subtrees plus one.
The "balance factor of R" refers to the right subtree of the tree which is out of balance, I suppose.
Okay, epiphany time.
Consider what a rotation does. Let's think about a left rotation.
Now, the big thing you have to notice here - this left rotation HAS NOT CHANGED THE DEPTH OF THE TREE. We're no more balanced for having done it.
But - and here's the magic in AVL - if we rotated the right child to the right FIRST, what we'd have is this...
And NOW if we rotate O left, what we get is this...
Magic! we've managed to get rid of a level of the tree - we've made the tree balance.
Balancing the tree means getting rid of excess depth, and packing the upper levels more completely - which is exactly what we've just done.
That whole stuff about single/double rotations is simply that you have to have your subtree looking like this;
before you rotate - and you may have to do a right rotate to get into that state. But if you're already in that state, you only need to do the left rotate.
You do not need to calculate tree depths on the fly.
You can maintain them as you perform operations.
Furthermore, you don't actually in fact have to maintain track of depths; you can simply keep track of the difference between the left and right tree depths.
http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_avl.aspx
Just keeping track of the balance factor (difference between left and right subtrees) is I found easier from a programming POV, except that sorting out the balance factor after a rotation is a PITA...