LeetCode 110 - Balanced Binary Tree


https://leetcode.com/problems/balanced-binary-tree/
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as:
a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
Example 1:
Given the following tree [3,9,20,null,null,15,7]:
    3
   / \
  9  20
    /  \
   15   7
Return true.

Example 2:
Given the following tree [1,2,2,3,3,null,null,4,4]:
       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
Return false
https://discuss.leetcode.com/topic/7798/the-bottom-up-o-n-solution-would-be-better
2.The second method is based on DFS. Instead of calling depth() explicitly for each child node, we return the height of the current node in DFS recursion. When the sub tree of the current node (inclusive) is balanced, the function dfsHeight() returns a non-negative value as the height. Otherwise -1 is returned. According to the leftHeight and rightHeight of the two children, the parent node could check if the sub tree is balanced, and decides its return value.

If ld is negative, then you should return without executing the left recursive call.
it'd be better shortcircuited.
    public boolean isBalanced(TreeNode root) {
        return checkBalance(root) == -1 ? false : true;
    }
    
    // 1. If a subtree is hit as unbalanced, the whole tree is unbalanced. In this case, -1 is set as the return value.
    // 2. If the left subtree and the right subtree of a node are balanced, there are two more cases:
    // 2.1. The tree rooted at the node is unbalanced (the depth of its two subtrees differs by more than 1), as a result, -1 is returned.
    // 2.2 The tree rooted at the node is balanced, then the depth of the tree will be returned.
    public int checkBalance(TreeNode node){
        if (node == null) // case 2.2
            return 0;
            
        int left = checkBalance(node.left);
        if (left == -1) // check case 1
            return -1;
            
        int right = checkBalance(node.right);
        if (right == -1) // check case 1
            return -1;
        
        if (left - right > 1 || right - left > 1)
            return -1; // check case 2.1
        
        return (left > right ? left : right) + 1; // case 2.2
    }
Bottom Up Solution, which is O(n) time complexity
public boolean isBalanced(TreeNode root) {
    if (root == null) return true;
    int depth = depth(root);
    if (depth == -1) return false;
    else return true;
}
private int depth(TreeNode root) {
    if (root == null) return 0;
    int left = depth(root.left);
    int right = depth(root.right);
    if (left == -1 || right == -1 || Math.abs(left-right) > 1) return -1;
    return Math.max(left,right)+1;
}
 public boolean isBalanced(TreeNode root) {
  if (root == null)
   return true;
 
  if (getHeight(root) == -1)
   return false;
 
  return true;
 }
 
 public int getHeight(TreeNode root) {
  if (root == null)
   return 0;
 
  int left = getHeight(root.left);
  int right = getHeight(root.right);
 
  if (left == -1 || right == -1)
   return -1;
 
  if (Math.abs(left - right) > 1) {
   return -1;
  }
 
  return Math.max(left, right) + 1;
 
 }

https://discuss.leetcode.com/topic/3746/accepted-on-solution
We determine recursively the height of the root node but when the recursion is coming upwards we return UNBALANCED instead of the actual height if we know that the tree is already known to be unbalanced.
We visit each node just once thus it has linear time complexity.
private static final int UNBALANCED = -99;

public boolean isBalanced(TreeNode root) {
    if (root == null) {
        return true;
    }
    return getHeight(root) != UNBALANCED;
}

private int getHeight(TreeNode root) {
    if (root == null) {
        return -1;
    }
    int l = getHeight(root.left);
    int r = getHeight(root.right);
    if (l == UNBALANCED || r == UNBALANCED || Math.abs(l-r) > 1) {
        return UNBALANCED;
    }
    return 1 + Math.max(l,r);
}

X. https://discuss.leetcode.com/topic/3323/use-exception-handling-to-early-terminate-and-return-once-a-unbalance-is-found
    public boolean isBalanced(TreeNode root) {
        try {
            getDepth(root);
        } catch (Exception e) {
            return false;
        }
        return true;
    }
    private int getDepth(TreeNode root) throws Exception {
        if (root == null) {
            return 0;
        }
        int leftDepth = getDepth(root.left);
        int rightDepth = getDepth(root.right);
        if (Math.abs(leftDepth - rightDepth) > 1) {
            throw new Exception("Not balanced!");
        }
        return Math.max(leftDepth, rightDepth) + 1;
    }

X. Iterative version
https://discuss.leetcode.com/topic/9346/a-iterative-postorder-traversal-java-solution
    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        Map<TreeNode, Integer> map = new HashMap<TreeNode, Integer>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            if((node.left==null || node.left!=null && map.containsKey(node.left)) &&(node.right==null || node.right!=null && map.containsKey(node.right))){
                int left = node.left==null?0:map.get(node.left);
                int right = node.right==null?0:map.get(node.right);
                if(Math.abs(left-right) > 1) return false;
                map.put(node, Math.max(left, right)+1);
            }else{
                if(node.left!=null && !map.containsKey(node.left)){
                    stack.push(node);
                    stack.push(node.left);
                }else{
                    stack.push(node);
                    stack.push(node.right);
                }
            }
        }
        return true;
    }
}

http://n00tc0d3r.blogspot.com/2013/01/determine-height-balanced-binary-tree.html
Attempt 1: The basic logic of isBalanced(root) could be:
  1. Find out the depths of left and right subtrees;
  2. If it is not balanced (i.e. abs(depLeft - depRight) > 1), return false;
  3. Otherwise, return isBalanced(root.left) && isBalanced(root.right);
Of course, we need a base case so that recursion can terminate: return true if it is an empty tree (i.e. root == null).

This seems fine and it works. But, think about it more carefully: Starting from root, we trace down to its left and right subtrees to get the depth, do the calculation, then in the next recursion, we go to its left and right child, repeat the same process (and revisit all children nodes!). That is, we need to access each node multiple times. More specifically, suppose we have a full binary tree of height k,
  • in top level, we visit each node once;
  • in 2nd level, visit twice;
  • ...
  • in (k-1)-th level, visit (k-1) times;
  • in k-th level, visit k time.
In total, we have T = k*(n/2), where n is the total number of nodes in the tree. We know k = log(n/2) = O(logn). So, this algorithm runs in time O(nlogn).

Can we do better? When we try to calculate the depth recursively, can we reuse any information without revisit?
 public boolean isBalanced(TreeNode root) {
        if(root == null){
         return true;
        }
        // 如果子树高度差大于1,则不平衡
        if(Math.abs(depth(root.left)-depth(root.right)) > 1){
         return false;
        }
        // 递归检查左子树和右子树的平衡性
        return isBalanced(root.left) && isBalanced(root.right);
    }
 // 帮助方法,返回树的高度
 private int depth(TreeNode root){
  if(root == null){
   return 0;
  }
  return 1 + Math.max(depth(root.left), depth(root.right));
 }

Attempt 2: In the helper function, getDepth(root):
  1. (base case) If it is an empty tree, return 0;
  2. Find out the depth of left and right subtrees, recursively;
  3. If it is not balanced or subtrees are not balanced, return a special value as a flag to pass back to upper levels;
  4. Otherwise, return the depth of the tree;
Notice that the difference from attempt 1 is that this time we check the balance along the way to calculate the depth of the tree. So, we don't need to revisit the nodes.
Since we visit each node constant times, this algorithm runs in time O(n) and use O(n) space for recursion.
10:    private int getHeight(TreeNode root) {  
11:      if (root == null) return 0;  
12:      int depL = getHeight(root.left);  
13:      int depR = getHeight(root.right);  
14:      if (depL < 0 || depR < 0 || Math.abs(depL - depR) > 1) return -1;  
15:      else return Math.max(depL, depR) + 1;  
16:    }  
17:    public boolean isBalanced(TreeNode root) {  
18:      return (getHeight(root) >= 0);  
19:    } 
http://www.jiuzhang.com/solutions/balanced-binary-tree/
// Version 1: with ResultType
class ResultType {
    public boolean isBalanced;
    public int maxDepth;
    public ResultType(boolean isBalanced, int maxDepth) {
        this.isBalanced = isBalanced;
        this.maxDepth = maxDepth;
    }
}

public class Solution {
    /**
     * @param root: The root of binary tree.
     * @return: True if this Binary tree is Balanced, or false.
     */
    public boolean isBalanced(TreeNode root) {
        return helper(root).isBalanced;
    }
    
    private ResultType helper(TreeNode root) {
        if (root == null) {
            return new ResultType(true, 0);
        }
        
        ResultType left = helper(root.left);
        ResultType right = helper(root.right);
        
        // subtree not balance
        if (!left.isBalanced || !right.isBalanced) {
            return new ResultType(false, -1);
        }
        
        // root not balance
        if (Math.abs(left.maxDepth - right.maxDepth) > 1) {
            return new ResultType(false, -1);
        }
        
        return new ResultType(true, Math.max(left.maxDepth, right.maxDepth) + 1);
    }
}

X. Use global variable
https://discuss.leetcode.com/topic/10192/java-o-n-solution-based-on-maximum-depth-of-binary-tree
private boolean result = true;

public boolean isBalanced(TreeNode root) {
    maxDepth(root);
    return result;
}

public int maxDepth(TreeNode root) {
    if (root == null)
        return 0;
    int l = maxDepth(root.left);
    int r = maxDepth(root.right);
    if (Math.abs(l - r) > 1)
        result = false;
    return 1 + Math.max(l, r);
}

for each node,the two subtrees differ in height by no more than one. We can implement a solution based on this definition.
We can simply recurse through the entire tree, and for each node, compute the heights of each subtree

X. O(N^2)
https://discuss.leetcode.com/topic/1278/can-we-have-a-better-solution
public boolean isBalanced(TreeNode root) {
    if (root == null)
        return true;
    if (Math.abs(depth(root.left) - depth(root.right)) >1)
        return false;
    return isBalanced(root.left) && isBalanced(root.right);
}

private int depth(TreeNode root){
    if (root == null)
        return 0;
    return Math.max(depth(root.left), depth(root.right)) + 1;
}
Although this works. it's not very efficient. On each node. we recurse through its entire subtree. This means that getHeight is called repeatedly on the same nodes. The algorithm isO(N log N) since each node is"touched" once per node above it.

This code runs in O(N) time and O(H) space, where H is the height of the tree.

If we inspect this method, we may notice that getHeight could actually check if the tree is balanced at
the same time as it's checking heights. What do we do when we discover that the subtree isn' t balanced?
Just return an error code.
This improved algorithm works by checking the height of each subtree as we recurse down from the root.
On each node, we recursively get the heights of the left and right subtrees through the checkHeight
method. If the subtree is balanced, then checkHeight will return the actual height of the subtree. If the
subtree is not balanced, then c h ec kHeight will return an error code. We will immediately break and
return an error code from the current call.
I What do we use for an error code? The height of a null tree is generally defined to be -1, so that's

not a great idea for an error code. Instead, we' ll use Integer. MIN_ VALUE.
int checkHeight(TreeNode root) {
        if (root == null) return -1;

        int leftHeight = checkHeight(root.left);
        if (leftHeight == Integer.MIN_VALUE) return Integer.MIN_VALUE; // Pass error up

        int rightHeight = checkHeight(root.right);
        if (rightHeight == Integer.MIN_VALUE) return Integer.MIN_VALUE; // Pass error up

        int heightDiff = leftHeight - rightHeight;
        if (Math.abs(heightDiff) > 1) {
                return Integer.MIN_VALUE; // Found error -> pass it back
        } else {
                return Math.max(leftHeight, rightHeight) + 1;
        }
}

boolean isBalanced(TreeNode root) {
        return checkHeight(root) != Integer.MIN_VALUE;
}
Read full article from 水中的鱼: [LeetCode] Balanced Binary Tree Solution

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts