117

It's been a while from those school years. Got a job as IT specialist at a hospital. Trying to move to do some actual programming now. I'm working on binary trees now, and I was wondering what would be the best way to determine if the tree is height-balanced.

I was thinking of something along this:

public boolean isBalanced(Node root){
    if(root==null){
        return true;  //tree is empty
    }
    else{
        int lh = root.left.height();
        int rh = root.right.height();
        if(lh - rh > 1 || rh - lh > 1){
            return false;
        }
    }
    return true;
}

Is this a good implementation? or am I missing something?

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
user69514
  • 26,935
  • 59
  • 154
  • 188

28 Answers28

170

Stumbled across this old question while searching for something else. I notice that you never did get a complete answer.

The way to solve this problem is to start by writing a specification for the function you are trying to write.

Specification: A well-formed binary tree is said to be "height-balanced" if (1) it is empty, or (2) its left and right children are height-balanced and the height of the left tree is within 1 of the height of the right tree.

Now that you have the specification, the code is trivial to write. Just follow the specification:

IsHeightBalanced(tree)
    return (tree is empty) or 
           (IsHeightBalanced(tree.left) and
            IsHeightBalanced(tree.right) and
            abs(Height(tree.left) - Height(tree.right)) <= 1)

Translating that into the programming language of your choice should be trivial.

Bonus exercise: this naive code sketch traverses the tree far too many times when computing the heights. Can you make it more efficient?

Super bonus exercise: suppose the tree is massively unbalanced. Like, a million nodes deep on one side and three deep on the other. Is there a scenario in which this algorithm blows the stack? Can you fix the implementation so that it never blows the stack, even when given a massively unbalanced tree?

UPDATE: Donal Fellows points out in his answer that there are different definitions of 'balanced' that one could choose. For example, one could take a stricter definition of "height balanced", and require that the path length to the nearest empty child is within one of the path to the farthest empty child. My definition is less strict than that, and therefore admits more trees.

One can also be less strict than my definition; one could say that a balanced tree is one in which the maximum path length to an empty tree on each branch differs by no more than two, or three, or some other constant. Or that the maximum path length is some fraction of the minimum path length, like a half or a quarter.

It really doesn't matter usually. The point of any tree-balancing algorithm is to ensure that you do not wind up in the situation where you have a million nodes on one side and three on the other. Donal's definition is fine in theory, but in practice it is a pain coming up with a tree-balancing algorithm that meets that level of strictness. The performance savings usually does not justify the implementation cost. You spend a lot of time doing unnecessary tree rearrangements in order to attain a level of balance that in practice makes little difference. Who cares if sometimes it takes forty branches to get to the farthest leaf in a million-node imperfectly-balanced tree when it could in theory take only twenty in a perfectly balanced tree? The point is that it doesn't ever take a million. Getting from a worst case of a million down to a worst case of forty is usually good enough; you don't have to go all the way to the optimal case.

RBT
  • 24,161
  • 21
  • 159
  • 240
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    Answer to the "exercises" below… – Potatoswatter Feb 02 '10 at 02:56
  • Answered Bonus exercise below. – Brian Feb 02 '10 at 14:25
  • sdk's answer below seems to be right and only makes 2 tree traversals so is O(n). Unless I'm missing somethinig, does that not solve at least your first bonus question. You can of course also use dynamic programming and your solution to cache intermediate heights – Aly May 10 '11 at 13:01
  • Theoretically, I would have to side with Donal Fellows' definition still. – Dhruv Gairola May 06 '12 at 16:30
  • Ok, I think lucky's answer meets all the requirements of both bonus and super-bonus - it's iterative (using a queue) and not recursive - so stack won't be overflowed and we move all memory-issue to heap. Also, it moves level by level, so if a tree is grossly unbalanced to 1 side, it will find it really soon (soonest?). – Maverick Meerkat Mar 19 '18 at 13:33
30

Balance is a truly subtle property; you think you know what it is, but it's so easy to get wrong. In particular, even Eric Lippert's (good) answer is off. That's because the notion of height is not enough. You need to have the concept of minimum and maximum heights of a tree (where the minimum height is the least number of steps from the root to a leaf, and the maximum is... well, you get the picture). Given that, we can define balance to be:

A tree where the maximum height of any branch is no more than one more than the minimum height of any branch.

(This actually implies that the branches are themselves balanced; you can pick the same branch for both maximum and minimum.)

All you need to do to verify this property is a simple tree traversal keeping track of the current depth. The first time you backtrack, that gives you a baseline depth. Each time after that when you backtrack, you compare the new depth against the baseline

  • if it's equal to the baseline, then you just continue
  • if it is more than one different, the tree isn't balanced
  • if it is one off, then you now know the range for balance, and all subsequent depths (when you're about to backtrack) must be either the first or the second value.

In code:

class Tree {
    Tree left, right;
    static interface Observer {
        public void before();
        public void after();
        public boolean end();
    }
    static boolean traverse(Tree t, Observer o) {
        if (t == null) {
            return o.end();
        } else {
            o.before();
            try {
                if (traverse(left, o))
                    return traverse(right, o);
                return false;
            } finally {
                o.after();
            }
        }
    }
    boolean balanced() {
        final Integer[] heights = new Integer[2];
        return traverse(this, new Observer() {
            int h;
            public void before() { h++; }
            public void after() { h--; }
            public boolean end() {
                if (heights[0] == null) {
                    heights[0] = h;
                } else if (Math.abs(heights[0] - h) > 1) {
                    return false;
                } else if (heights[0] != h) {
                    if (heights[1] == null) {
                        heights[1] = h;
                    } else if (heights[1] != h) {
                        return false;
                    }
                }
                return true;
            }
        });
    }
}

I suppose you could do this without using the Observer pattern, but I find it easier to reason this way.


[EDIT]: Why you can't just take the height of each side. Consider this tree:

        /\
       /  \
      /    \
     /      \_____
    /\      /     \_
   /  \    /      / \
  /\   C  /\     /   \
 /  \    /  \   /\   /\
A    B  D    E F  G H  J

OK, a bit messy, but each side of the root is balanced: C is depth 2, A, B, D, E are depth 3, and F, G, H, J are depth 4. The height of the left branch is 2 (remember the height decreases as you traverse the branch), the height of the right branch is 3. Yet the overall tree is not balanced as there is a difference in height of 2 between C and F. You need a minimax specification (though the actual algorithm can be less complex as there should be only two permitted heights).

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • Ah, good point. You could have a tree which is h(LL)=4, h(LR)=3, h(RL)=3, h(RR)=2. Thus, h(L)=4 and h(R)=3, so it would appear balanced to the earlier algorithm, but with a max/min depth of 4/2, this is not balanced. This would probably make more sense with a picture. – Tim Apr 07 '10 at 22:04
  • 1
    That's what I've just added (with the world's nastiest ASCII graphic tree). – Donal Fellows Apr 07 '10 at 22:10
  • @DonalFellows: you mentioned the height of the left branch is 2. but the left branch has 4 nodes including the root and leaf A. The height will be 3 in this case correct – brain storm Oct 28 '13 at 19:16
  • "A tree where the maximum height of any branch is no more than one more than the minimum height of any branch.". This definition is not correct. One example case is- A--(Right branch)---B---(Right branch)---C Here, maximum depth & minimum depth both equals 2 yet its not a balanced binary tree – Yash Verma Jun 30 '21 at 23:43
23

This only determines if the top level of the tree is balanced. That is, you could have a tree with two long branches off the far left and far right, with nothing in the middle, and this would return true. You need to recursively check the root.left and root.right to see if they are internally balanced as well before returning true.

Jesse Rusak
  • 56,530
  • 12
  • 101
  • 102
  • However, if the code had a max and min height method, if it's globally balanced it would be locally balanced as well. – Ari Mar 13 '15 at 04:27
22

Post order solution, traverse the tree only once. Time complexity is O(n), space is O(1), it's better than top-down solution. I give you a java version implementation.

public static <T> boolean isBalanced(TreeNode<T> root){
    return checkBalance(root) != -1;
}

private static <T> int checkBalance(TreeNode<T> node){
    if(node == null) return 0;
    int left = checkBalance(node.getLeft());

    if(left == -1) return -1;

    int right = checkBalance(node.getRight());

    if(right == -1) return -1;

    if(Math.abs(left - right) > 1){
        return -1;
    }else{
        return 1 + Math.max(left, right);
    }
}
Jiaji Li
  • 1,259
  • 12
  • 14
  • 4
    nice solution, but space complexity should be O(H) where H is the height of the tree. This is because stack allocation for recursion. – legrass Aug 15 '15 at 10:43
  • What does `left == -1` mean? When would that ever be the case? Do we assume the recursive call implies that `left == -1` is true if all the subtrees of the left children are unbalanced? – Adrienne Jan 13 '16 at 18:49
  • `left == 1` means left subtree is unbalanced, then the whole tree is unbalanced. We do not have to check right subtree anymore, and can return `-1`. – tning Feb 22 '16 at 02:15
  • Time complexity is O(n) because you have to go through all the elements. And, if you had x nodes and it will take y time to check balance; if you had 2x nodes, it will take 2y time to check balance. That all sound right? – Jack May 15 '16 at 05:24
  • Well explanation with drawing is here: https://algorithms.tutorialhorizon.com/find-whether-if-a-given-binary-tree-is-balanced/ – Shir Sep 14 '18 at 12:24
22

Bonus exercise response. The simple solution. Obviously in a real implementation one might wrap this or something to avoid requiring the user to include height in their response.

IsHeightBalanced(tree, out height)
    if (tree is empty)
        height = 0
        return true
    balance = IsHeightBalanced(tree.left, out heightleft) and IsHeightBalanced(tree.right, out heightright)
    height = max(heightleft, heightright)+1
    return balance and abs(heightleft - heightright) <= 1     
Brian
  • 25,523
  • 18
  • 82
  • 173
  • If the tree is bigger than a few hundred layers high, you get a stackoverflow exception. You've done it efficiently, but it does not handle medium or large size data sets. – Eric Leschinski May 03 '15 at 20:35
  • Is this pseudocode you just came up with or is it a real language? (I mean the "`out height`" variable notation) – kap Apr 26 '17 at 15:27
  • @kap: This is pseudocode, but the [out syntax](https://msdn.microsoft.com/en-us/library/t3c3bfhx.aspx) is taken from C#. Basically, it means the parameter travels from the called function to the caller (as opposed to standard parameters, which travel from the caller to the called function or ref parameters, which travel from the caller to the called function and back). This effectively allows functions to return more than one value. – Brian Apr 27 '17 at 13:11
  • would love to see where `heightleft` and `heightright` comes from – Jac Frall Mar 31 '21 at 00:44
  • @JacFrall: The second parameter to `IsHeightBalanced` is an `out` parameter. E.g., `IsHeightBalanced(tree.left, heightleft)` is not being passed a value for `heightleft` but rather is computing `heightleft`. I've edited my answer to make this use of `out` parameters a bit more explicit. – Brian Mar 31 '21 at 12:57
15

The definition of a height-balanced binary tree is:

Binary tree in which the height of the two subtrees of every node never differ by more than 1.

So, An empty binary tree is always height-balanced.
A non-empty binary tree is height-balanced if:

  1. Its left subtree is height-balanced.
  2. Its right subtree is height-balanced.
  3. The difference between heights of left & right subtree is not greater than 1.

Consider the tree:

    A
     \ 
      B
     / \
    C   D

As seen the left subtree of A is height-balanced (as it is empty) and so is its right subtree. But still the tree is not height-balanced as condition 3 is not met as height of left-subtree is 0 and height of right sub-tree is 2.

Also the following tree is not height balanced even though the height of left and right sub-tree are equal. Your existing code will return true for it.

       A
     /  \ 
    B    C
   /      \
  D        G
 /          \
E            H

So the word every in the def is very important.

This will work:

int height(treeNodePtr root) {
        return (!root) ? 0: 1 + MAX(height(root->left),height(root->right));
}

bool isHeightBalanced(treeNodePtr root) {
        return (root == NULL) ||
                (isHeightBalanced(root->left) &&
                isHeightBalanced(root->right) &&
                abs(height(root->left) - height(root->right)) <=1);
}

Ideone Link

codaddict
  • 445,704
  • 82
  • 492
  • 529
  • So this answer helped me a lot. However, I found the free [MIT intro to algorithms course] seems to contradict condition 3. Page 4 shows a RB tree where the height of the left branch is 2 and the right branch is 4. Can you offer some clarification to me? Perhaps I don't get the definition of a subtree. [1]:http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/video-lectures/lecture-10-red-black-trees-rotations-insertions-deletions/lec10.pdf – i8abug Aug 30 '11 at 17:45
  • The difference seems to come from this definition in the course notes. All simple paths from any node x to a descendant leaf have the same number of black nodes = black-height(x) – i8abug Aug 30 '11 at 17:54
  • Just to follow up, I found a definition that changes point (3) in your answer to "every leaf is 'not more than a certain distance' away from the root than any other leaf". This seems to satisfy both cases. [Here](http://user.it.uu.se/~justin/Teaching/AD1/Slides/AVLtrees.pdf) is the link from some random course ware – i8abug Aug 31 '11 at 03:26
8

If binary tree is balanced or not can be checked by Level order traversal:

private boolean isLeaf(TreeNode root) {
    if (root.left == null && root.right == null)
        return true;
    return false;
}

private boolean isBalanced(TreeNode root) {
    if (root == null)
        return true;
    Vector<TreeNode> queue = new Vector<TreeNode>();
    int level = 1, minLevel = Integer.MAX_VALUE, maxLevel = Integer.MIN_VALUE;
    queue.add(root);
    while (!queue.isEmpty()) {
        int elementCount = queue.size();
        while (elementCount > 0) {
            TreeNode node = queue.remove(0);
            if (isLeaf(node)) {
                if (minLevel > level)
                    minLevel = level;
                if (maxLevel < level)
                    maxLevel = level;
            } else {
                if (node.left != null)
                    queue.add(node.left);
                if (node.right != null)
                    queue.add(node.right);
            }
            elementCount--;
        }
        if (abs(maxLevel - minLevel) > 1) {
            return false;
        }
        level++;
    }

    return true;
}
lucky
  • 164
  • 1
  • 5
  • 2
    Excellent answer. I think it meets all the requirement's that Eric posted regarding Bonus and Super-Bonus. It's iterative (using a queue) and not recursive - so call-stack won't be overflowed and we move all memory-issues to heap. It doesn't even requires traversing the entire tree. It moves level by level, so if a tree is grossly unbalanced to 1 side, it will find it really soon (soonest? well sooner than most recursive algorithms, though you could implement a post-order traversal iterative algorithm which will find last-level unbalances sooner but will act poorer on first levels). So +1 :-) – Maverick Meerkat Mar 19 '18 at 13:40
  • It does not work for a few cases like: 1->2->3 Check https://leetcode.com/problems/balanced-binary-tree/ – Davide Pugliese Jan 22 '23 at 13:32
7

This is being made way more complicated than it actually is.

The algorithm is as follows:

  1. Let A = depth of the highest-level node
  2. Let B = depth of the lowest-level node

  3. If abs(A-B) <= 1, then the tree is balanced

Mike
  • 71
  • 1
  • 1
  • 3
    Two problems, it's not as efficient as it could be, you are making two passes over the entire tree. And for trees that have one node to the left, and thousands to the right, you unnecessarily step through the entire thing, when you could have stopped after 3 checks. – Eric Leschinski May 03 '15 at 20:38
5

What balanced means depends a bit on the structure at hand. For instance, A B-Tree cannot have nodes more than a certain depth from the root, or less for that matter, all data lives at a fixed depth from the root, but it can be out of balance if the distribution of leaves to leaves-but-one nodes is uneven. Skip-lists Have no notion at all of balance, relying instead on probability to achieve decent performance. Fibonacci trees purposefully fall out of balance, postponing the rebalance to achieve superior asymptotic performance in exchange for occasionally longer updates. AVL and Red-Black trees attach metadata to each node to attain a depth-balance invariant.

All of these structures and more are present in the standard libraries of most common programming systems (except python, RAGE!). Implementing one or two is good programming practice, but its probably not a good use of time to roll your own for production, unless your problem has some peculiar performance need not satisfied by any off-the-shelf collections.

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
4

Note 1: The height of any sub-tree is computed only once.

Note 2: If the left sub-tree is unbalanced then the computation of the right sub-tree, potentially containing million elements, is skipped.

// return height of tree rooted at "tn" if, and only if, it is a balanced subtree
// else return -1
int maxHeight( TreeNode const * tn ) {
    if( tn ) {
        int const lh = maxHeight( tn->left );
        if( lh == -1 ) return -1;
        int const rh = maxHeight( tn->right );
        if( rh == -1 ) return -1;
        if( abs( lh - rh ) > 1 ) return -1;
        return 1 + max( lh, rh );
    }
    return 0;
}

bool isBalanced( TreeNode const * root ) {
    // Unless the maxHeight is -1, the subtree under "root" is balanced
    return maxHeight( root ) != -1;
}
Arun
  • 19,750
  • 10
  • 51
  • 60
3

Here is a complete worked out tested solution in C# (sorry I'm no java dev) (just copy paste in console app). I know that definition of balanced varies so not everyone may like my test results but please just look at the slightly different approach of checking depth/height in a recursive loop and exiting on the first mismatch without saving node height/level/depth on each node (only maintaining it in a function call).

using System;
using System.Linq;
using System.Text;

namespace BalancedTree
{
    class Program
    {
        public static void Main()
        {
            //Value Gathering
            Console.WriteLine(RunTreeTests(new[] { 0 }));
            Console.WriteLine(RunTreeTests(new int[] { }));

            Console.WriteLine(RunTreeTests(new[] { 0, 1, 2, 3, 4, -1, -4, -3, -2 }));
            Console.WriteLine(RunTreeTests(null));
            Console.WriteLine(RunTreeTests(new[] { 10, 8, 12, 8, 4, 14, 8, 10 }));
            Console.WriteLine(RunTreeTests(new int[] { 20, 10, 30, 5, 15, 25, 35, 3, 8, 12, 17, 22, 27, 32, 37 }));

            Console.ReadKey();
        }

        static string RunTreeTests(int[] scores)
        {
            if (scores == null || scores.Count() == 0)
            {
                return null;
            }

            var tree = new BinarySearchTree();

            foreach (var score in scores)
            {
                tree.InsertScore(score);
            }

            Console.WriteLine(tree.IsBalanced());

            var sb = tree.GetBreadthWardsTraversedNodes();

            return sb.ToString(0, sb.Length - 1);
        }
    }

    public class Node
    {
        public int Value { get; set; }
        public int Count { get; set; }
        public Node RightChild { get; set; }
        public Node LeftChild { get; set; }
        public Node(int value)
        {
            Value = value;
            Count = 1;
        }

        public override string ToString()
        {
            return Value + ":" + Count;
        }

        public bool IsLeafNode()
        {
            return LeftChild == null && RightChild == null;
        }

        public void AddValue(int value)
        {
            if (value == Value)
            {
                Count++;
            }
            else
            {
                if (value > Value)
                {
                    if (RightChild == null)
                    {
                        RightChild = new Node(value);
                    }
                    else
                    {
                        RightChild.AddValue(value);
                    }
                }
                else
                {
                    if (LeftChild == null)
                    {
                        LeftChild = new Node(value);
                    }
                    else
                    {
                        LeftChild.AddValue(value);
                    }
                }
            }
        }
    }

    public class BinarySearchTree
    {
        public Node Root { get; set; }

        public void InsertScore(int score)
        {
            if (Root == null)
            {
                Root = new Node(score);
            }
            else
            {
                Root.AddValue(score);
            }
        }

        private static int _heightCheck;
        public bool IsBalanced()
        {
            _heightCheck = 0;
            var height = 0;
            if (Root == null) return true;
            var result = CheckHeight(Root, ref height);
            height--;
            return (result && height == 0);
        }

        private static bool CheckHeight(Node node, ref int height)
        {
            height++;
            if (node.LeftChild == null)
            {
                if (node.RightChild != null) return false;
                if (_heightCheck != 0) return _heightCheck == height;
                _heightCheck = height;
                return true;
            }
            if (node.RightChild == null)
            {
                return false;
            }

            var leftCheck = CheckHeight(node.LeftChild, ref height);
            if (!leftCheck) return false;
            height--;
            var rightCheck = CheckHeight(node.RightChild, ref height);
            if (!rightCheck) return false;
            height--;
            return true;
        }


        public StringBuilder GetBreadthWardsTraversedNodes()
        {
            if (Root == null) return null;
            var traversQueue = new StringBuilder();
            traversQueue.Append(Root + ",");
            if (Root.IsLeafNode()) return traversQueue;
            TraversBreadthWards(traversQueue, Root);
            return traversQueue;
        }

        private static void TraversBreadthWards(StringBuilder sb, Node node)
        {
            if (node == null) return;
            sb.Append(node.LeftChild + ",");
            sb.Append(node.RightChild + ",");
            if (node.LeftChild != null && !node.LeftChild.IsLeafNode())
            {
                TraversBreadthWards(sb, node.LeftChild);
            }
            if (node.RightChild != null && !node.RightChild.IsLeafNode())
            {
                TraversBreadthWards(sb, node.RightChild);
            }
        }
    }
}
sbp
  • 913
  • 8
  • 19
  • I don't understand how someone could negative vote this answer within 2 minutes of posting answer?? Negative vote is fine but could you please explain what's wrong with this solution? – sbp Sep 05 '16 at 11:54
3
public boolean isBalanced(TreeNode root)
{
    return (maxDepth(root) - minDepth(root) <= 1);
}

public int maxDepth(TreeNode root)
{
    if (root == null) return 0;

    return 1 + max(maxDepth(root.left), maxDepth(root.right));
}

public int minDepth (TreeNode root)
{
    if (root == null) return 0;

    return 1 + min(minDepth(root.left), minDepth(root.right));
}
sdk
  • 31
  • 1
  • I think this solution is not correct.If you pass a tree that has a single node i.e. a root it will return as maxDepth `1` (same for minDepth). The correct depth though should be `0`.A tree's root has always `0` depth – Cratylus Dec 11 '11 at 14:26
3

Balancing usually depends on the length of the longest path on each direction. The above algorithm is not going to do that for you.

What are you trying to implement? There are self-balancing trees around (AVL/Red-black). In fact, Java trees are balanced.

Uri
  • 88,451
  • 51
  • 221
  • 321
2

If this is for your job, I suggest:

  1. do not reinvent the wheel and
  2. use/buy COTS instead of fiddling with bits.
  3. Save your time/energy for solving business problems.
Steven A. Lowe
  • 60,273
  • 18
  • 132
  • 202
2
#include <iostream>
#include <deque>
#include <queue>

struct node
{
    int data;
    node *left;
    node *right;
};

bool isBalanced(node *root)
{
    if ( !root)
    {
        return true;
    }

    std::queue<node *> q1;
    std::queue<int>  q2;
    int level = 0, last_level = -1, node_count = 0;

    q1.push(root);
    q2.push(level);

    while ( !q1.empty() )
    {
        node *current = q1.front();
        level = q2.front();

        q1.pop();
        q2.pop();

        if ( level )
        {
            ++node_count;
        }

                if ( current->left )
                {
                        q1.push(current->left);
                        q2.push(level + 1);
                }

                if ( current->right )
                {
                        q1.push(current->right);
                        q2.push(level + 1);
                }

        if ( level != last_level )
        {
            std::cout << "Check: " << (node_count ? node_count - 1 : 1) << ", Level: " << level << ", Old level: " << last_level << std::endl;
            if ( level && (node_count - 1) != (1 << (level-1)) )
            {
                return false;
            }

            last_level = q2.front();
            if ( level ) node_count = 1;
        }
    }

    return true;
}

int main()
{
    node tree[15];

    tree[0].left  = &tree[1];
    tree[0].right = &tree[2];
    tree[1].left  = &tree[3];
    tree[1].right = &tree[4];
    tree[2].left  = &tree[5];
    tree[2].right = &tree[6];
    tree[3].left  = &tree[7];
    tree[3].right = &tree[8];
    tree[4].left  = &tree[9];   // NULL;
    tree[4].right = &tree[10];  // NULL;
    tree[5].left  = &tree[11];  // NULL;
    tree[5].right = &tree[12];  // NULL;
    tree[6].left  = &tree[13];
    tree[6].right = &tree[14];
    tree[7].left  = &tree[11];
    tree[7].right = &tree[12];
    tree[8].left  = NULL;
    tree[8].right = &tree[10];
    tree[9].left  = NULL;
    tree[9].right = &tree[10];
    tree[10].left = NULL;
    tree[10].right= NULL;
    tree[11].left = NULL;
    tree[11].right= NULL;
    tree[12].left = NULL;
    tree[12].right= NULL;
    tree[13].left = NULL;
    tree[13].right= NULL;
    tree[14].left = NULL;
    tree[14].right= NULL;

    std::cout << "Result: " << isBalanced(tree) << std::endl;

    return 0;
}
Frank Raaj
  • 21
  • 1
1

Here is a version based on a generic depth-first traversal. Should be faster than the other correct answer and handle all the mentioned "challenges." Apologies for the style, I don't really know Java.

You can still make it much faster by returning early if max and min are both set and have a difference >1.

public boolean isBalanced( Node root ) {
    int curDepth = 0, maxLeaf = 0, minLeaf = INT_MAX;
    if ( root == null ) return true;
    while ( root != null ) {
        if ( root.left == null || root.right == null ) {
            maxLeaf = max( maxLeaf, curDepth );
            minLeaf = min( minLeaf, curDepth );
        }
        if ( root.left != null ) {
            curDepth += 1;
            root = root.left;
        } else {
            Node last = root;
            while ( root != null
             && ( root.right == null || root.right == last ) ) {
                curDepth -= 1;
                last = root;
                root = root.parent;
            }
            if ( root != null ) {
                curDepth += 1;
                root = root.right;
            }
        }
    }
    return ( maxLeaf - minLeaf <= 1 );
}
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 1
    A nice attempt but it clearly does not work. Let x be a null node. Let a non-null tree node be denoted as (LEFT VALUE RIGHT). Consider the tree (x A (x B x)). "root" points to nodes A, B, A, B, A, B ... forever. Care to try again? A hint: it's actually easier *without* parent pointers. – Eric Lippert Feb 02 '10 at 03:18
  • @Eric: Oops, fixed (I think). Well, I'm trying to do this without O(depth) memory, and if the structure doesn't have parent pointers (it often does), you need to use a stack. – Potatoswatter Feb 02 '10 at 04:50
  • So what you're telling me is you'd rather use O(n) permanent memory in parent pointers to avoid allocating O(d) temporary memory, where log n <= d <= n ? This seems like a false economy. – Eric Lippert Feb 02 '10 at 06:20
  • Unfortunately, though you've fixed the problem with the traversal, there's a far bigger problem here. This does not test whether a tree is balanced, it tests whether a tree has all its leaves close to the same level. That's not the definition of "balanced" that I gave. Consider the tree ((((x D x) C x) B x) A x). Your code reports that this is "balanced" when it obviously is maximally unbalanced. Care to try again? – Eric Lippert Feb 02 '10 at 06:37
  • @Eric reply 1: not a false economy if you already use the parent pointers for something else. reply 2: sure, why not. This is a bizarre way of debugging… I shouldn't be blindly writing traversals of anything at 4 am… – Potatoswatter Feb 02 '10 at 10:48
  • Also, note that a height-balanced tree can have leaves whose depths differ by two. For example, ((((x H x) D x) B (x E x)) A (((x I x) F x) C ((x J x) G (x K (x L x))))) is height balanced, but the depth of leaf E is two less than the depth of L. – Eric Lippert Feb 02 '10 at 17:41
  • @Eric: There are various definitions of balance. A red-black tree's leaves can vary in depth by a factor of two. You can adjust the `return` expression accordingly. – Potatoswatter Feb 02 '10 at 20:13
  • Indeed, there are many definitions of balance. However, it's still not the case that you can work out whether a given tree is balanced according to my definition solely by locating the deepest and shallowest leaves. I can construct you height-balanced trees where the deepest and shallowest leaves are arbitrarily far apart, given enough nodes. Now, if you disagree with my claim that your definition of height-balanced is not the same as mine then I encourage you to attempt a formal proof that they are equivalent. – Eric Lippert Feb 02 '10 at 21:13
  • @Eric: Absolutely, they are not equivalent. Not all trees with max_leaf_depth <= 2 * min_leaf_depth are valid red-black trees! However, that's beyond the scope of the OP. I used the most stringent definition of balance, which happens to be universal. As for practicality, I confess I don't know the application of validating a tree's balance without simultaneously balancing it. – Potatoswatter Feb 02 '10 at 21:29
  • You can fix your implementation by tracking the depth of not the lowest and highest *leaves* but the lowest and highest *nodes that have any null child*. You'll still be testing for a different definition of "balanced" than the one I proposed; as you note, yours is a considerably more restricting definition. It is an interesting challenge to try and implement a tester for my more relaxed definition without consuming unbounded call stack space. – Eric Lippert Feb 02 '10 at 23:04
  • Also, the reason to validate a tree's balance without balancing it is to enable you to write a test suite that verifies that your tree balancer is correct! – Eric Lippert Feb 02 '10 at 23:05
  • @Eric: That is the change I applied last night. As for an AVL tester with O(1) memory (given parent links), just use this answer as a template for an O(1)-memory depth-first traversal, and perform a nested depth-first traversal at each node to find the heights of its subtrees. That will use O(1) memory (but O(N) parent links) and O(N^2) time. – Potatoswatter Feb 02 '10 at 23:44
  • @Eric: interestingly, the parent links may actually optimize something in that algorithm: the checking algorithm may be multithreaded and share a common set of parent links, potentially using *less* memory than a multithreaded version where each thread has a stack. – Potatoswatter Feb 02 '10 at 23:48
  • Ah, so you did. Clearly I did not read the change carefully enough! – Eric Lippert Feb 03 '10 at 00:31
1

RE: @lucky's solution using a BFS to do a level-order traversal.

We traverse the tree and keep a reference to vars min/max-level which describe the minimum level at which a node is a leaf.

I believe that the @lucky solution requires a modification. As suggested by @codaddict, rather than checking if a node is a leaf, we must check if EITHER the left or right children is null (not both). Otherwise, the algorithm would consider this a valid balanced tree:

     1
    / \
   2   4
    \   \
     3   1

In Python:

def is_bal(root):
    if root is None:
        return True

    import queue

    Q = queue.Queue()
    Q.put(root)

    level = 0
    min_level, max_level = sys.maxsize, sys.minsize

    while not Q.empty():
        level_size = Q.qsize()

        for i in range(level_size):
            node = Q.get()

            if not node.left or node.right:
                min_level, max_level = min(min_level, level), max(max_level, level)

            if node.left:
                Q.put(node.left)
            if node.right:
                Q.put(node.right)

        level += 1

        if abs(max_level - min_level) > 1:
            return False

    return True

This solution should satisfy all the stipulations provided in the initial question, operating in O(n) time and O(n) space. Memory overflow would be directed to the heap rather than blowing a recursive call-stack.

Alternatively, we could initially traverse the tree to compute + cache max heights for each root subtree iteratively. Then in another iterative run, check if the cached heights of the left and right subtrees for each root never differ by more than one. This would also run in O(n) time and O(n) space but iteratively so as not to cause stack overflow.

vikasnair
  • 39
  • 3
  • It seems your decision is wrong, check the tree: Node root1 = new Node(1); root1.left = new Node(2); root1.right = new Node(3); root1.left.left = new Node(4); root1.left.right = new Node(5); root1.right.right = new Node(6); root1.left.left.left = new Node(7); root1.left.left.right = new Node(8); – Megaprog May 19 '22 at 07:01
  • Does not work for a few cases. I tested it on against this set of unit tests: https://leetcode.com/problems/balanced-binary-tree/ – Davide Pugliese Jan 22 '23 at 13:25
1
class Node {
    int data;
    Node left;
    Node right;

    // assign variable with constructor
    public Node(int data) {
        this.data = data;
    }
}

public class BinaryTree {

    Node root;

    // get max depth
    public static int maxDepth(Node node) {
        if (node == null)
            return 0;

        return 1 + Math.max(maxDepth(node.left), maxDepth(node.right));
    }

    // get min depth
    public static int minDepth(Node node) {
        if (node == null)
            return 0;

        return 1 + Math.min(minDepth(node.left), minDepth(node.right));
    }

    // return max-min<=1 to check if tree balanced
    public boolean isBalanced(Node node) {

        if (Math.abs(maxDepth(node) - minDepth(node)) <= 1)
            return true;

        return false;
    }

    public static void main(String... strings) {
        BinaryTree tree = new BinaryTree();
        tree.root = new Node(1);
        tree.root.left = new Node(2);
        tree.root.right = new Node(3);


        if (tree.isBalanced(tree.root))
            System.out.println("Tree is balanced");
        else
            System.out.println("Tree is not balanced");
    }
}
Pranay
  • 69
  • 4
1

Well, you need a way to determine the heights of left and right, and if left and right are balanced.

And I'd just return height(node->left) == height(node->right);

As to writing a height function, read: Understanding recursion

Community
  • 1
  • 1
tpdi
  • 34,554
  • 11
  • 80
  • 120
1

What kind of tree are you talking about? There are self-balancing trees out there. Check their algorithms where they determine if they need to reorder the tree in order to maintain balance.

lothar
  • 19,853
  • 5
  • 45
  • 59
0
public int height(Node node){
    if(node==null)return 0;
    else{
        int l=height(node.leftChild);
        int r=height(node.rightChild);
       return(l>r?l+1:r+1);

}}
public boolean balanced(Node n){

    int l= height(n.leftChild);
    int r= height(n.rightChild);

    System.out.println(l + " " +r);
    if(Math.abs(l-r)>1)
        return false;
    else 
        return true;
    }
always
  • 1
0

An empty tree is height-balanced. A non-empty binary tree T is balanced if:

1) Left subtree of T is balanced

2) Right subtree of T is balanced

3) The difference between heights of left subtree and right subtree is not more than 1.

/* program to check if a tree is height-balanced or not */
#include<stdio.h>
#include<stdlib.h>
#define bool int

/* A binary tree node has data, pointer to left child
   and a pointer to right child */
struct node
{
  int data;
  struct node* left;
  struct node* right;
};

/* The function returns true if root is balanced else false
   The second parameter is to store the height of tree.  
   Initially, we need to pass a pointer to a location with value 
   as 0. We can also write a wrapper over this function */
bool isBalanced(struct node *root, int* height)
{
  /* lh --> Height of left subtree 
     rh --> Height of right subtree */   
  int lh = 0, rh = 0;  

  /* l will be true if left subtree is balanced 
    and r will be true if right subtree is balanced */
  int l = 0, r = 0;

  if(root == NULL)
  {
    *height = 0;
     return 1;
  }

  /* Get the heights of left and right subtrees in lh and rh 
    And store the returned values in l and r */   
  l = isBalanced(root->left, &lh);
  r = isBalanced(root->right,&rh);

  /* Height of current node is max of heights of left and 
     right subtrees plus 1*/   
  *height = (lh > rh? lh: rh) + 1;

  /* If difference between heights of left and right 
     subtrees is more than 2 then this node is not balanced
     so return 0 */
  if((lh - rh >= 2) || (rh - lh >= 2))
    return 0;

  /* If this node is balanced and left and right subtrees 
    are balanced then return true */
  else return l&&r;
}


/* UTILITY FUNCTIONS TO TEST isBalanced() FUNCTION */

/* Helper function that allocates a new node with the
   given data and NULL left and right pointers. */
struct node* newNode(int data)
{
    struct node* node = (struct node*)
                                malloc(sizeof(struct node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;

    return(node);
}

int main()
{
  int height = 0;

  /* Constructed binary tree is
             1
           /   \
         2      3
       /  \    /
     4     5  6
    /
   7
  */   
  struct node *root = newNode(1);  
  root->left = newNode(2);
  root->right = newNode(3);
  root->left->left = newNode(4);
  root->left->right = newNode(5);
  root->right->left = newNode(6);
  root->left->left->left = newNode(7);

  if(isBalanced(root, &height))
    printf("Tree is balanced");
  else
    printf("Tree is not balanced");    

  getchar();
  return 0;
}

Time Complexity: O(n)

Saqlain
  • 17,490
  • 4
  • 27
  • 33
0

To have a better performance specially on huge trees you can save the height in each node so it is a trade off space Vs performance:

class Node {
    Node left;
    Node right;
    int value;
    int height;
}

Example of implementing the addition and same for deletion

void addNode(Node root,int v)
{    int height =0;
     while(root != null)
     {
         // Since we are adding new node so the height 
         // will increase by one in each node we will pass by
         root.height += 1;
         height++;
         else if(v > root.value){
            root = root.left();
            }
         else{
         root = root.right();
         }

     }

         height++;
         Node n = new Node(v , height);
         root = n;         
}
int treeMaxHeight(Node root)
{
 return Math.Max(root.left.height,root.right.height);
}

int treeMinHeight(Node root)
{
 return Math.Min(root.left.height,root.right.height);

}

Boolean isNodeBlanced(Node root)
{
   if (treeMaxHeight(root) - treeMinHeight(root) > 2)
       return false;

  return true;
}

Boolean isTreeBlanced (Node root)
{
    if(root == null || isTreeBalanced(root.left) && isTreeBalanced(root.right) && isNodeBlanced(root))
    return true;

  return false;

}
0

Here's what i have tried for Eric's bonus exercise. I try to unwind of my recursive loops and return as soon as I find a subtree to be not balanced.

int heightBalanced(node *root){
    int i = 1;
    heightBalancedRecursive(root, &i);
    return i; 
} 

int heightBalancedRecursive(node *root, int *i){

    int lb = 0, rb = 0;

    if(!root || ! *i)  // if node is null or a subtree is not height balanced
           return 0;  

    lb = heightBalancedRecursive(root -> left,i);

    if (!*i)         // subtree is not balanced. Skip traversing the tree anymore
        return 0;

    rb = heightBalancedRecursive(root -> right,i)

    if (abs(lb - rb) > 1)  // not balanced. Make i zero.
        *i = 0;

    return ( lb > rb ? lb +1 : rb + 1); // return the current height of the subtree
}
Sudharsanan
  • 567
  • 2
  • 6
  • 11
0

Recursively check if each subtree is balanced or not. Recursion means, you ask something to subtree and subtree will return a response. You keep asking till you hit the base case. in tree questions base case is when you hit the leaf node.

In this case, we ask 2 questions to a subtree:

1- Are you balanced?

2- What is your height?

So recursive function will return 2 answers, and I keep those answers in the array.

This is python code:

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        def dfs(root):
            # leaf node is balanced
            if not root:
                # we carry the height of each subtree
                return [True,0]
            # with recursion, we start from bottom, so we do not have repetitive work
            left,right=dfs(root.left),dfs(root.right)
            # if any of the subtree return false, then we know entire tree is not balanced
            balanced=left[0] and right[0] and abs(left[1]-right[1])<=1
            # 1+max(left[1],right[1]) is the height of the each subtree. 1 is the root of the subtree
            return [balanced,1+max(left[1],right[1])]
        return dfs(root)[0]

this is javascript code:

var isBalanced = function(root) {
 function dfs(root){
     if(!root){
         return [true,0]
     }
     
     const left=dfs(root.left)
     const right=dfs(root.right)
     const balanced=left[0] && right[0] && Math.abs(left[1]-right[1])<=1
     return [balanced,1+Math.max(left[1],right[1])]
 }
    return dfs(root)[0]
};
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
0

a height-balanced binary tree is a binary tree in which the depth of the two subtrees of each node does not differ by more than one

here's the tree node

public class TreeNode
{
    public int Value;
    public TreeNode Left;
    public TreeNode Right;

    public TreeNode(int value)
    {
        Value = value;
    }
}
    public bool IsBalanced(TreeNode root)
    {
        if(root == null)
        {
            return true;
        }
        
        var left = Depth(root.Left);
        var right = Depth(root.Right);
        
        if(Math.Abs(left - right) > 1)
        {
            return false;
        }
        
        return IsBalanced(root.Left) && IsBalanced(root.Right);
    }
    
    private int Depth(TreeNode node)
    {
        if(node == null)
        {
            return 0;
        }
        return Math.Max(Depth(node.Left), Depth(node.Right)) + 1;
    }

you can try to practice here

GSerjo
  • 4,725
  • 1
  • 36
  • 55
-1
    static boolean isBalanced(Node root) {
    //check in the depth of left and right subtree
    int diff = depth(root.getLeft()) - depth(root.getRight());
    if (diff < 0) {
        diff = diff * -1;
    }
    if (diff > 1) {
        return false;
    }
    //go to child nodes
    else {
        if (root.getLeft() == null && root.getRight() == null) {
            return true;
        } else if (root.getLeft() == null) {
            if (depth(root.getRight()) > 1) {
                return false;
            } else {
                return true;
            }
        } else if (root.getRight() == null) {
            if (depth(root.getLeft()) > 1) {
                return false;
            } else {
                return true;
            }
        } else if (root.getLeft() != null && root.getRight() != null && isBalanced(root.getLeft()) && isBalanced(root.getRight())) {
            return true;
        } else {
            return false;
        }
    }
}
-2

Wouldn't this work?

return ( ( Math.abs( size( root.left ) - size( root.right ) ) < 2 );

Any unbalanced tree would always fail this.

Sumit Kishore
  • 191
  • 1
  • 4