How to determine if binary tree is balanced?

前端 未结 27 1476
萌比男神i
萌比男神i 2020-11-30 16:11

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, a

相关标签:
27条回答
  • 2020-11-30 16:47

    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).

    0 讨论(0)
  • 2020-11-30 16:48

    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.

    0 讨论(0)
  • 2020-11-30 16:49

    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;
    }
    
    0 讨论(0)
  • 2020-11-30 16:49

    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.

    0 讨论(0)
  • 2020-11-30 16:51

    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.

    0 讨论(0)
  • 2020-11-30 16:52

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题