How are you representing your tree? It looks to me that
l(_)
represents the empty tree, and
node(L,R)
represents a non-empty tree.
And I suspect that your height/2
has a bug in that you seem to have defined the height of an empty tree as being 1 (rather than 0).
I would probably represent a binary tree as follows:
so that one might represent the tree
a
/ \
b c
/ / \
d e f
as
tree( a ,
tree( b ,
tree( d , nil , nil ) ,
nil
) ,
tree( c ,
tree( e , nil , nil ) ,
tree( f , nil , nil )
) .
and a leaf node (a tree with no subtrees) looks something like
tree( data , nil , nil )
Determination of Balance
So, working from that representation, and from the definition
A binary tree is balanced if:
- Its left sub-tree is balanced
- Its right sub-tree is balanced
- The respective heights of the sub-trees differ by no more than 1
We can easily write a descriptive solution to the problem:
is_balanced( nil ) . % the empty tree is balanced
is_balanced( tree(_,L,R) ) :- % a non-empty tree is balanced IF ...
is_balanced(L) , % - the left sub-tree is balanced
is_balanced(R) , % - the right sub-tree is balanced
tree_height(L,LH) , % - the height of the left sub-tree
tree_height(R,RH) , % - the height of the right sub-tree
abs( LH - RH ) < 2 % - differ by no more than 1
. % Right?
We just need to compute the height of a tree.
Computation of Height
One can compute the height of such a tree as follows:
tree_height( nil , 0 ) . % the depth of an empty tree is zero.
tree_height( tree(_,L,R) , H ) :- % for a non-empty tree...
tree_height( L , LH ) , % - we compute the height of the left subtree
tree_height( R , RH ) , % - we compute the height of the right subtree
H is 1 + max( LH , RH ) % - the overall height is 1 more than the higher of the two values thus obtained.
. % Right?
Efficiency
One might note that there
- seems to be a lot of tree traversals happening, and
is_balanced/2
has a suspicious resemblance to tree_height/2
.
Therefore, one might optimize things by blending the two and computing depth on the fly:
Edited: Added wrapper predicate is_balanced/1
:
is_balanced( T ) :- is_balanced( T, _ ) .
is_balanced( nil , 0 ) . % the empty tree is balanced and has a height of zero.
is_balanced( tree(_,L,R) , H ) :- % a non-empty tree is balanced IF ...
is_balanced( L , LH ) , % - its left subtree is balanced, and
is_balanced( R , RH ) , % - its right subtree is balanced, and
abs( LH - RH) < 2 , % - their respective heights differ by no more than 1, and
H is 1 + max( LH , RH ) % - the current tree's height is 1 more than the larger of the heights of the two subtrees.
. % Easy! (And elegant!)