Monday, August 25, 2014

Leetcode: Word Break II

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.
Return all such possible sentences.
For example, given
s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].
A solution is ["cats and dog", "cat sand dog"].

Understand the Problem:
The problem asks for partition a string such that each part is included in the dictionary. In addition, you are asked for returning all possible sentences. The mainly difference between work break I is, in that question you are only required to return if such a partition existed. In this problem, however, you need to return all possible sentences. 

Recursive Solution:
We first consider the recursive solution. The recursive solution is very similar to the work break I, the mainly difference is we save all intermediate results.

Code (Java):
public class Solution {
    public ArrayList<String> wordBreak(String s, Set<String> dict) {
        ArrayList<String> result = new ArrayList<String>();
        if (s == null || s.isEmpty() || dict.isEmpty()) 
            return result;
        
        wordBreakHelper(s, dict, "", result);
        
        return result;
    }
    
    private void wordBreakHelper(String s, Set<String> dict, String item, ArrayList<String> result) {
        if (s.isEmpty()) {
            result.add(item);
            return;
        }
        
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            sb.append(s.charAt(i));
            if (dict.contains(sb.toString())) {
                String newItem = item.length() > 0 ? item + " " + sb.toString() : sb.toString();
                wordBreakHelper(s.substring(i + 1), dict, newItem, result);
            }
        }
    }
}

Discussion:
The time complexity is still O(2^n), because we partitioned the string into two substrings and recursively solve the problem. Not surprisingly, it got TLE error at OJ.

An Accepted Solution:
The previous solution exceeds the time limits of OJ is because there is a test case containing very long string which is not breakable. So one solution is for each recursion, we check if the string is breakable at first, using the method in Word Break I. If not, we don't need to do the following computations. 

Code (Java):
public class Solution {
    public ArrayList<String> wordBreak(String s, Set<String> dict) {
        ArrayList<String> result = new ArrayList<String>();
        if (s == null || s.isEmpty() || dict.isEmpty()) 
            return result;
        
        wordBreakHelper(s, dict, "", result);
        
        return result;
    }
    
    private void wordBreakHelper(String s, Set<String> dict, String item, ArrayList<String> result) {
        if (!isBreakable(s, dict)) return;
        
        if (s.isEmpty()) {
            result.add(item);
            return;
        }
        
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            sb.append(s.charAt(i));
            if (dict.contains(sb.toString())) {
                String newItem = item.length() > 0 ? item + " " + sb.toString() : sb.toString();
                wordBreakHelper(s.substring(i + 1), dict, newItem, result);
            }
        }
    }
    
    private boolean isBreakable(String s, Set<String> dict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] == true && dict.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        
        return dp[s.length()];
    }
}

Update on 11/10/14:
import java.util.*;

public class Solution {
    public List<String> wordBreak(String s, Set<String> dict) {
        List<String> result = new ArrayList<String>();
        
        if (s == null || s.length() == 0 || dict == null || dict.size() == 0) {
            return result;
        }
        
        List<String> curr = new ArrayList<String>();
        wordBreakHelper(0, s, dict, curr, result);
        
        return result;
    }
    
    private void wordBreakHelper(int start, String s, Set<String> dict, List<String> curr, List<String> result) {
        if (start >= s.length()) {
            String temp = constructString(curr);
            result.add(temp);
        }
        
        for (int i = start; i < s.length(); i++) {
            if (dict.contains(s.substring(start, i + 1))) {
                curr.add(s.substring(start, i + 1));
                wordBreakHelper(i + 1, s, dict, curr, result);
                curr.remove(curr.size() - 1);
            }
        }
    }
    
    private String constructString(List<String> tokens) {
        StringBuilder sb = new StringBuilder();
        
        for (int i = 0; i < tokens.size() - 1; i++) {
            sb.append(tokens.get(i) + " ");
        }
        
        sb.append(tokens.get(tokens.size() - 1));
        
        return sb.toString();
    }

    public static void main(String[] args) {
      Solution sol = new Solution();

      String s = "catsanddog";
      Set<String> dict = new HashSet<String>();
      dict.add("cat");
      dict.add("cats");
      dict.add("and");
      dict.add("sand");
      dict.add("dog");

      List<String> result = sol.wordBreak(s, dict);

      for (String str : result) {
        System.out.println("Result is " + str);
      }
    }
}

Update on 5/2/19: Memorization
The reason to use memo is think of one example
lint code
li nt code
l int code

dict = lint, li, nt, l, int 
So we check the "code" repeatedly whereas it is not in the dict.

public class Solution {
    /*
     * @param s: A string
     * @param wordDict: A set of words.
     * @return: All possible sentences.
     */
    public List<String> wordBreak(String s, Set<String> wordDict) {
        // write your code here
        if (s == null || s.length() == 0 || wordDict == null || wordDict.size() == 0) {
            return new ArrayList<String>();
        }

        Map<String, List<String>> memo = new HashMap<>();
        
        return wordBreakHelper(s, wordDict, memo);
    }
    
    private List<String> wordBreakHelper(String s, Set<String> wordDict, Map<String, List<String>> memo) {
        if (memo.containsKey(s)) {
            return memo.get(s);
        }

        List<String> ans = new ArrayList<>();

        if (wordDict.contains(s)) {
            ans.add(s);
        }

        for (int i = 1; i <= s.length(); i++) {
            String prefix = s.substring(0, i);
            if (wordDict.contains(prefix)) {
                String suffix = s.substring(i);
                
                List<String> suffixAns = wordBreakHelper(suffix, wordDict, memo);
                
                for (String word : suffixAns) {
                    ans.add(prefix + " " + word);
                }
            }
        }

        memo.put(s, ans);

        return ans;
    }
}




Leetcode: Word Break

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s = "leetcode",
dict = ["leet", "code"].
Return true because "leetcode" can be segmented as "leet code".
Understand the Problem:
The problem asks for giving a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words. 

As a first glance of the problem, it asks for partition a word s, such that both its two parts are included in the dictionary. 

Wrong Solution:
A straight-forward solution could be developed using iterative approach. We partition the word into two parts, and check both of the two parts are included in the dictionary. We start from the first character of the string, and check the both halves. If yes, return true, else go to the next character. 

Wrong Code (Java):
public class Solution {
    public boolean wordBreak(String s, Set<string> dict) {
        if (s.isEmpty()) return true;
        
        int len = s.length();
        
        for (int i = 0; i < len; i++) {
            String s1 = s.substring(0, i);
            String s2 = s.substring(i, len);
            
            if (s1.isEmpty() && dict.contains(s2)) return true;
            
            if (dict.contains(s1) && dict.contains(s2)) {
                return true;
            }
        }
        return false;
    }
}

Why the solution above is wrong? Let's see an output at first:
Input:"abcd", ["a","abc","b","cd"]
Output:false
Expected:true
Now you might get the reason. We only considered partitioning the string into TWO parts. However, the problem asked for partition the word into one or MORE substrings. Knowing that, the problem becomes not that easy. 

Let's rethink the naive solution above, why it is not doable?
If we partition the string into two substrings, the complexity is O(n)
For three parts, it is O(n^2)
For four parts, it is O(n^3)
For a string with length n, the maximal possible partitions is n+1 substrings, i.e., each character is a substring plus a empty string. So the overall time complexity is bounded by O(n^(n), which is unable to solve in polynomial time. So we must come out a practical solution.



Correct Solution(Recursive):
Note that in the above solution, we only considered the string to be partitioned into two halves. To address this, we could use DFS in recursive manner. For the substring [0, i) is contained in the dictionary, we recursively start from [i, len) to check the rest of it. 

Code (Java):
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if (dict.contains(s)) return true;
        
        int len = s.length();
        
        for (int i = 1; i < len; i++) {
            String s1 = s.substring(0, i);
            if (dict.contains(s1)) {
                String s2 = s.substring(i, len);
                if (wordBreak(s2, dict) == true) return true;
            }
        }
        return false;
    }
}

The solution itself has no problem. However, it will result in Time Limit Exceed at OJ. Let's analyze the time complexity of this solution. For each recursion, we have to check its left and right halves, so two operations per recursion. We do this recursion O(n) times. So the total time complexity is O(2^n). Of course it will exceed the time limit. 

Before we jump into an accepted solution, let's see another solution. Basically it is the same as above, but used the dictionary to compare with the string.

Alternative Solution (Java):
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        return wordBreak(s, dict, 0);   
    }
    
    private boolean wordBreak(String s, Set<String> dict, int start) {
        if (start == s.length()) return true;
        
        for (String word : dict) {
            int len = word.length();
            
            if (start + len >= s.length()) continue;
            
            if (s.substring(start, start + len).equals(word)) {
                if (wordBreak(s, dict, start + len) == true) return true;
            }
        }
        return false;
    }
}
 
Note that it will start result in TLE, but the idea of comparing dictionary word with the string is quite commonly used in many sub-string problems. 


DP Solution:
The key idea of using DP is using a status array dp[] to mark the status such that dp[i] is true means [0, i) is segmented using the dictionary. So s[0, i) can be segmented if and only if s[0, j) can be segmented AND s[j, i) is in the dictionary. 


public class Solution {
    public boolean wordBreak(String s, Set dict) {
        if (s == null || s.isEmpty()) return true;
        
        // dp[i] means [0,i) is breakable
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && dict.contains(s.substring(j,i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

Discussion:
So using DP, the time complexity is reduced to O(n^2). The space complexity is O(n) since we used the status array. 

Alternative Solution using DP:
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if (s == null || s.isEmpty()) return true;
        
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        
        for (int i = 0; i < s.length(); i++) {
            if (!dp[i]) continue;
            
            for (String word : dict) {
                int len = word.length();
                int end = i + len;
                if (end > s.length()) continue;
                
                if (dp[end]) continue;
                
                if (s.substring(i, end).equals(word)) {
                    dp[end] = true;
                }
            }
        }
        return dp[s.length()];
    }
}

Summary:
This is the first DP problem we ever met. The key of using DP is to cache the intermediate results. Try to understand how DP could reduce the time complexity. 

Leetcode: Word Break

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s = "leetcode",
dict = ["leet", "code"].
Return true because "leetcode" can be segmented as "leet code".
Understand the Problem:
The problem asks for giving a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words. 

As a first glance of the problem, it asks for partition a word s, such that both its two parts are included in the dictionary. 

Wrong Solution:
A straight-forward solution could be developed using iterative approach. We partition the word into two parts, and check both of the two parts are included in the dictionary. We start from the first character of the string, and check the both halves. If yes, return true, else go to the next character. 

Wrong Code (Java):
public class Solution {
    public boolean wordBreak(String s, Set<string> dict) {
        if (s.isEmpty()) return true;
        
        int len = s.length();
        
        for (int i = 0; i < len; i++) {
            String s1 = s.substring(0, i);
            String s2 = s.substring(i, len);
            
            if (s1.isEmpty() && dict.contains(s2)) return true;
            
            if (dict.contains(s1) && dict.contains(s2)) {
                return true;
            }
        }
        return false;
    }
}

Why the solution above is wrong? Let's see an output at first:
Input:"abcd", ["a","abc","b","cd"]
Output:false
Expected:true
Now you might get the reason. We only considered partitioning the string into TWO parts. However, the problem asked for partition the word into one or MORE substrings. Knowing that, the problem becomes not that easy. 

Let's rethink the naive solution above, why it is not doable?
If we partition the string into two substrings, the complexity is O(n)
For three parts, it is O(n^2)
For four parts, it is O(n^3)
For a string with length n, the maximal possible partitions is n+1 substrings, i.e., each character is a substring plus a empty string. So the overall time complexity is bounded by O(n^(n), which is unable to solve in polynomial time. So we must come out a practical solution.



Correct Solution(Recursive):
Note that in the above solution, we only considered the string to be partitioned into two halves. To address this, we could use DFS in recursive manner. For the substring [0, i) is contained in the dictionary, we recursively start from [i, len) to check the rest of it. 

Code (Java):
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if (dict.contains(s)) return true;
        
        int len = s.length();
        
        for (int i = 1; i < len; i++) {
            String s1 = s.substring(0, i);
            if (dict.contains(s1)) {
                String s2 = s.substring(i, len);
                if (wordBreak(s2, dict) == true) return true;
            }
        }
        return false;
    }
}

The solution itself has no problem. However, it will result in Time Limit Exceed at OJ. Let's analyze the time complexity of this solution. For each recursion, we have to check its left and right halves, so two operations per recursion. We do this recursion O(n) times. So the total time complexity is O(2^n). Of course it will exceed the time limit. 

Before we jump into an accepted solution, let's see another solution. Basically it is the same as above, but used the dictionary to compare with the string.

Alternative Solution (Java):
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        return wordBreak(s, dict, 0);   
    }
    
    private boolean wordBreak(String s, Set<String> dict, int start) {
        if (start == s.length()) return true;
        
        for (String word : dict) {
            int len = word.length();
            
            if (start + len >= s.length()) continue;
            
            if (s.substring(start, start + len).equals(word)) {
                if (wordBreak(s, dict, start + len) == true) return true;
            }
        }
        return false;
    }
}
 
Note that it will start result in TLE, but the idea of comparing dictionary word with the string is quite commonly used in many sub-string problems. 


DP Solution:
The key idea of using DP is using a status array dp[] to mark the status such that dp[i] is true means [0, i) is segmented using the dictionary. So s[0, i) can be segmented if and only if s[0, j) can be segmented AND s[j, i) is in the dictionary. 


public class Solution {
    public boolean wordBreak(String s, Set dict) {
        if (s == null || s.isEmpty()) return true;
        
        // dp[i] means [0,i) is breakable
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && dict.contains(s.substring(j,i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

Discussion:
So using DP, the time complexity is reduced to O(n^2). The space complexity is O(n) since we used the status array. 

Alternative Solution using DP:
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if (s == null || s.isEmpty()) return true;
        
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        
        for (int i = 0; i < s.length(); i++) {
            if (!dp[i]) continue;
            
            for (String word : dict) {
                int len = word.length();
                int end = i + len;
                if (end > s.length()) continue;
                
                if (dp[end]) continue;
                
                if (s.substring(i, end).equals(word)) {
                    dp[end] = true;
                }
            }
        }
        return dp[s.length()];
    }
}

Summary:
This is the first DP problem we ever met. The key of using DP is to cache the intermediate results. Try to understand how DP could reduce the time complexity. 

Thursday, August 21, 2014

Leetcode: Recover Binary Search Tree

Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?


Understand the problem:
As described in the problem, two elements of a BST are swapped by mistake. Recover the tree without changing its structure. There are two requirements in the problem. The first is we cannot change the structure of the tree. The second is do it in-place.


Solution:
If a binary tree is a BST, its inorder traversal should be in ascending order. We may use this property to solve this problem. Consider the following case:
For the inorder traversal list 123456, if 2 and 6 are swapped, the list becomes
163452. So when we scan the list, our goal is to mark the two swapped numbers and swap them back. So we can use two pointers, p and q. We compare the current value with the previous one, so when we see the first descending value, which is 3, the p pointer points to the previous node before 3, which is 6. When the next time we see the descending order again, which is 2, we use q pointer points to 2. Then we can swap the two numbers. 

Do we consider all the case? Actually there is still a case we did not consider. For the two numbers in adjacent, e.g. 123456, where 34 are swapped, so now it is 124356. The first time it encounters a descending order is at 3, where we point p to 4, the one before 3, then there is no other descending later. So where is q? So we should point q to this current position. 

Code (Java): 
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    TreeNode p,q;
    TreeNode prev = null;
    public void recoverTree(TreeNode root) {
        
        inOrderTraversal(root);
        
        int temp = p.val;
        p.val = q.val;
        q.val = temp;
    }
    
    private void inOrderTraversal(TreeNode root) {
        if (root == null) return;
        inOrderTraversal(root.left);
        
        if (prev != null && root.val < prev.val) {
            if (p == null) {
                p = prev;
                q = root;
            } else {
                q = root;
            }
        }
        prev = root;
        inOrderTraversal(root.right);
    }
}


Summary:
The problem looks tricky at the first glance. But at least you know the inorder traversal in ascending order for BST, the problem will become doable. The crux of this problem is to consider the two cases where swapped nodes are adjacent or not, and use two pointers pointing to the two nodes.   

Leetcode: Same Tree

Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.


Understand the problem:

The problem asks for checking if two trees are equal or not. Two binary trees are considered equal if they are structurally identical and the nodes have the same value.  

Recursive Solution:
Note that for two trees are identical, both each node and the tree structures should be the same. So for each node, we first check if two node values are the same. Then we check both nodes have the same structure for left child and right child. We recursively repeat this process until we traversed all nodes of the tree.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        return isSame(p, q);
    }
    
    private boolean isSame(TreeNode p, TreeNode q) {
        if (p == null) return q == null;
        if (q == null) return false;
        
        if (p.val != q.val) return false;
        
        if (isSame(p.left, q.left) == false) return false;
        if (isSame(p.right, q.right) == false) return false;
        
        return true;
    }
} 


Iterative Solution:
The iterative solution still utilized queues. However, if we follow the previous BFS way using a queue, we will falsely consider 1,2 and 1,#,2 as the same tree. Why? Because we don't allow null in the queue. If we allow null in the queue, the problem will be much easier. 
We maintain a queue for each tree respectively. We dequeue a node from each tree, and check if both are null, we pop a new node. If either is a null, we return false. If both are not null and with different value, we return false as well. Then we add its left and right child into the queue at last.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q == null) return true;
        
        Queue<TreeNode> lQueue = new LinkedList<TreeNode>();
        Queue<TreeNode> rQueue = new LinkedList<TreeNode>();
        
        lQueue.offer(p);
        rQueue.offer(q);
        if (lQueue.isEmpty() || rQueue.isEmpty()) return false;
        
        while (!lQueue.isEmpty() && !rQueue.isEmpty()) {
            TreeNode lCurr = lQueue.poll();
            TreeNode rCurr = rQueue.poll();
            
            if (lCurr == null && rCurr == null) continue;
            if (lCurr == null || rCurr == null) return false;
            
            if (lCurr.val != rCurr.val) return false;
            
            lQueue.offer(lCurr.left);
            lQueue.offer(lCurr.right);
            rQueue.offer(rCurr.left);
            rQueue.offer(rCurr.right);
        }
        
        
        return true;
    }
} 

Summary:
This question and the symmetric tree is the same kind of question. The recursive solution is very neat, but different from traditional DFS solution. The iterative solution using queue is a bit of tricky because we allow enqueue null into the queue, and utilized the null elements to check if the queue is the same. Be careful this idea. 

Leetcode: Symmetric Tree

Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree is symmetric:
    1
   / \
  2   2
 / \ / \
3  4 4  3
But the following is not:
    1
   / \
  2   2
   \   \
   3    3
Note:
Bonus points if you could solve it both recursively and iteratively.
Understand the problem:
The problem asks for checking if a tree is a mirror of itself, i.e, symmetric around its center. 
We can take several examples to show the problem:
Besides the  two examples shown above, 
An empty is symmetric. 
A tree with only 1 node is symmetric.
     1
    /  \
  3   4
is not symmetric.

Recursive Solution:
It is not hard to find that if a tree is symmetric, its inorder traversal list should be symmetric as well. For instance, for the first example, its inorder traversal is 3,2,4,1,4,2,3. Since the list is symmetric, the tree is symmetric as well.
For the second example, its inorder traversal is 2,3,1,2,3, which is not symmetric. 

So one straight-forward solution is to traverse the tree in-order ,save it to an array list. Then check the array list is symmetric. 

Wrong Answer:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        
        ArrayList<Integer> result = new ArrayList<Integer>();    
        inOrderTraversal(root, result);
        
        return isSymmetricArray(result);
    }
    
    private void inOrderTraversal(TreeNode root, ArrayList<Integer> result) {
        if (root == null) return;
        
        inOrderTraversal(root.left, result);
        result.add(root.val);
        inOrderTraversal(root.right, result);
    }
    
    private boolean isSymmetricArray(ArrayList<Integer> result) {
        int start = 0;
        int end = result.size() - 1;
        while (start < end) {
            if (result.get(start) != result.get(end)) return false;
            start++;
            end--;
        }
        return true;
    }
}

The code itself has nothing wrong, but the inorder traversal will result in the following wrong answer:
Input:{1,2,3,3,#,2,#}
Output:true
Expected:false

Correct Solution:
So we must come out another solution. For a symmetric tree, the left child and right child of the root must be equal. Then for those two nodes, the left node's left child must be equal to the right node's right child; the left node's right child must be equal to the right node's left child. Then we recursively repeats those process until both nodes are null.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        
        return isSymmetric(root.left, root.right);
    }
    
    private boolean isSymmetric(TreeNode a, TreeNode b) {
        if (a == null) return b == null;
        else if (b == null) return false;
        
        if (a.val != b.val) return false;
        
        if (isSymmetric(a.left, b.right) == false) return false;
        if (isSymmetric(a.right, b.left) == false) return false;
        
        return true;
    }
}

Iterative Solution:
The iterative solution could borrow the idea of BFS, so we still use a queue to store intermediate results. Instead of using a queue, we used two queues, the left queue stores the left children, while the right queue stores the right children. We start from the root node, and enqueue its left and right child into the right and right queue, respectively, if left and right child existed. Then like the BFS, we dequeue two elements from the left and right queue each, and check if it is equal. If not, return false. Then we enqueue the left node's left child and right node's right child into the corresponding queue. And we enqueue left node's right child and right node's left child into the queue. We repeat this process until the queues are empty. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        
        Queue<TreeNode> lQueue = new LinkedList<TreeNode>();
        Queue<TreeNode> rQueue = new LinkedList<TreeNode>();
        
        if (root.left != null) lQueue.offer(root.left);
        if (root.right != null) rQueue.offer(root.right);
        
        while (!lQueue.isEmpty() || !rQueue.isEmpty()) {
            int size = Math.max(lQueue.size(), rQueue.size());
            for (int i = 0; i < size; i++) {
                if (lQueue.isEmpty() || rQueue.isEmpty()) return false;
                
                TreeNode a = lQueue.poll();
                TreeNode b = rQueue.poll();
                
                if (a.val != b.val) return false;
                
                if (a.left != null) lQueue.offer(a.left);
                if (b.right != null) rQueue.offer(b.right);
                if (a.right != null) rQueue.offer(a.right);
                if (b.left != null) lQueue.offer(b.left);
            }
        }
        
        return true;
    }
}

Summary:
The crux of the problem is to know how to determine if a tree is symmetric. If you know to compare two nodes' left and right children respectively, the problem becomes quite easy. An error-prone approach was showed. The mainly reason to come out that approach is to categorize a question into either BFS or DFS. Analogy is definitely helpful, but it is more important to go back the nature of the problem, and analyze what the problem asks for on earth. So be very careful about understanding the problem and deducing your solution.


Update on 10/7/14:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        
        Queue lQueue = new LinkedList();
        Queue rQueue = new LinkedList();
        
        lQueue.offer(root);
        rQueue.offer(root);
        
        while (!lQueue.isEmpty() && !rQueue.isEmpty()) {
            TreeNode curLeft = lQueue.poll();
            TreeNode curRight = rQueue.poll();
            if (curLeft.val != curRight.val) {
                return false;
            }
            
            if (curLeft.left != null) {
                lQueue.offer(curLeft.left);
            }
            
            if (curRight.right != null) {
                rQueue.offer(curRight.right);
            }
            
            if (curLeft.right != null) {
                rQueue.offer(curLeft.right);
            }
            
            if (curRight.left != null) {
                lQueue.offer(curRight.left);
            }
        }
        
        if (!lQueue.isEmpty() || !rQueue.isEmpty()) {
            return false;
        } else {
            return true;
        }
        
    }
}

Update on 4/14/15:
Iterative solution with allowing to add null into the queue.
public class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        
        Queue<TreeNode> lQueue = new LinkedList<TreeNode>();
        Queue<TreeNode> rQueue = new LinkedList<TreeNode>();
        
        lQueue.offer(root.left);
        rQueue.offer(root.right);
        
        while (!lQueue.isEmpty()) {
            TreeNode a = lQueue.poll();
            TreeNode b = rQueue.poll();
            
            if (a == null && b != null) {
                return false;
            }
            
            if (b == null && a != null) {
                return false;
            }
            
            if (a != null && b != null && a.val != b.val) {
                return false;
            }
            
            if (a != null) {
                lQueue.offer(a.left);
                lQueue.offer(a.right);
            }
            
            if (b != null) {
                rQueue.offer(b.right);
                rQueue.offer(b.left);
            }
        }
        
        return true;
    }
}

Leetcode: Binary Tree Zigzag Level Order Traversal

Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its zigzag level order traversal as:
[
  [3],
  [20,9],
  [15,7]
]
confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.
Understand the Problem:
The problem can be still categorized into BFS problem. The only difference between the regular BFS is it traversed the tree in Zigzag order. 

Recursive Solution:
We can still solve this problem using the regular BFS solution. However, for the even level, we store the nodes at reverse order. At the odd level, we store at the forward order. Then there is no difference between regular BFS and zigZag BFS.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        
        zigzagLevelOrder(root, result, 1);
        
        return result;
    }
    
    private void zigzagLevelOrder(TreeNode root, ArrayList<ArrayList<Integer>> result, int level) {
        if (root == null) return;
        
        if (result.size() < level) {
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            if ((level % 2) == 1) {
                levelList.add(root.val);
            } else {
                levelList.add(0, root.val);
            }
            result.add(levelList);
        } else {
            if ((level % 2) == 1) {
                result.get(level - 1).add(root.val);
            } else {
                result.get(level - 1).add(0, root.val);
            }
        }
        
        zigzagLevelOrder(root.left, result, level + 1);
        zigzagLevelOrder(root.right, result, level + 1);
    }
}

Discussion:
The time complexity for the recursive solution is still O(n^2) since inserting the node at head in the even levels takes O(n) time instead of O(1). 

Iterative Solution:
The iterative solution is very similar to the iterative BFS solution. There are two major difference in order do address this zigzag BFS. One is to save the level information, because we wanna store the node in backward order for even levels. Second, for the even level, we may still insert the nodes at head, as the recursive solution. Or we may employ a stack, and for the dequeued elements from the queue, we push into the stack. At last we pop all elements and append to the array list.

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        if (root == null) return result;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        int level = 0;
        
        queue.offer(root);
        
        while(!queue.isEmpty()) {
            int size = queue.size();
            level++;
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            Stack<Integer> levelStack = new Stack<Integer>();
            
            for (int i = 0; i < size; i++) {
                TreeNode curr = queue.poll();
                if (level % 2 == 1) {
                    levelList.add(curr.val);
                } else {
                    levelStack.push(curr.val);
                }
                
                if (curr.left != null) {
                    queue.offer(curr.left);
                }
                
                if (curr.right != null) {
                    queue.offer(curr.right);
                }
            }
            
            // for even level, dump the elments from the stack to the arraylist
            if (level % 2 == 0) {
                while (!levelStack.isEmpty()) {
                    levelList.add(levelStack.pop());
                }
            }
            result.add(levelList);
        }
        
        return result;
    }
}


Discussion:
Since pushing and popping an element from the stack takes O(1) time, the overall time complexity is O(n). For the space complexity, since we employed a queue and stack to store the intermediate result, it takes space of O(n) + O(n) = O(n). So the space complexity is still O(n).

Summary:
So far you should be very familiar with all DFS and BFS traversal problems. The recursive solutions are straight-forward, but tricky to understand every detail. Be familiar with the iterative solution, and how queue, stack and pointers are used.

Update on 9/30/14:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (root == null) {
            return result;
        }
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        
        while(!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> levelList = new ArrayList<Integer>();
            if (result.size() % 2 == 0) {
                for (int i = 0; i < size; i++) {
                    TreeNode curr = queue.poll();
                    levelList.add(curr.val);
                    
                    if (curr.left != null) {
                        queue.offer(curr.left);
                    }
                    
                    if (curr.right != null) {
                        queue.offer(curr.right);
                    }
                }
            } else {
                Stack<Integer> stack = new Stack<Integer>();
                for (int i = 0; i < size; i++) {
                    TreeNode curr = queue.poll();
                    stack.push(curr.val);
                    
                    if (curr.left != null) {
                        queue.offer(curr.left);
                    }
                    
                    if (curr.right != null) {
                        queue.offer(curr.right);
                    }
                }
                
                while(!stack.isEmpty()) {
                    levelList.add(stack.pop());
                }
            }
            result.add(levelList);
        }
        
        return result;
    }
}

Wednesday, August 20, 2014

Leetcode: Binary Tree Level Order Traversal II

Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its bottom-up level order traversal as:
[
  [15,7],
  [9,20],
  [3]
]
confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.


Understand the problem:
The problem asks for traverse the binary tree in order. The mainly difference between the BFS is it asks for returning the result bottom-up.


Naive Solution:
One native solution is to still use the pre-order traversal of the tree, and save the level, like how we solved the BFS problem recursively. The major difference is instead of appending the list of each level, we add them at front of the result list, and move others forward. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrderBottom(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        
        levelOrderBottom(root, result, 1);
        
        return result;
    }
    
    private void levelOrderBottom(TreeNode root, ArrayList<ArrayList>Integer>> result, int level) {
        if (root == null) return;
        
        if (result.size() < level) {
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            levelList.add(root.val);
            result.add(0, levelList);
        } else {
            result.get(result.size() - level).add(root.val);
        }
        
        levelOrderBottom(root.left, result, level + 1);
        levelOrderBottom(root.right, result, level + 1);
    }
}


Discussion:
Now let's analyze the complexity. Traversing all nodes of the tree takes O(n) time. However, adding the level list at the beginning of the result list and moving the rest behind takes O(n) time as well. So the overall time complexity is O(n^2).

A Better Solution:
Now that we know the time complexity is O(n^2), can we traverse the tree bottom-up, then append it to the array will takes only O(1) time thus the overall time complexity would be O(n). So far I was unabA Better Solution:
Now that we know the time complexity is O(n^2), can we traverse the tree bottom-up, then append it to the array will takes only O(1) time thus the overall time complexity would be O(n). 

So far I was unable to find a method to traverse the tree in level order bottom-up. It is hard to address the following tree
      1
     /  \
   2   3
        /  \
      15  7
If do the post-order traverse, it will start from 2 instead of 15. 

Iterative Solution:
The iterative solution for BFS is to use a queue. The idea is for each node, we enqueue all its left and right children into the queue, and dequeue all of them in next level. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrderBottom(TreeNode root) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        if (root == null) return result;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            ArrayList<Integer> levelList = new ArrayList<Integer>();
            for (int i = 0; i < size; i++) {
                TreeNode curr = queue.poll();
                levelList.add(curr.val);
                
                if (curr.left != null) {
                    queue.offer(curr.left);
                }
                if (curr.right != null) {
                    queue.offer(curr.right);
                }
            }
            result.add(0, levelList);
        }                      
        return result;
    }
} 

Summary:
Note the iterative solution of the BFS, be very familiar with the details. In addition, one possible O(n) solution for the bottom-up traversal is to do the top-down traversal first and get the final result list. Then reverse the list in-place. Since reversing the list takes O(n) time as well, the overall time complexity is still O(n). 

Leetcode: Maximum Depth of Binary Tree

Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.


Understand of the problem:
The problem asks for finding the maximum depth of a binary tree. The maximum depth is defined as the number of nodes along the longest path from the root down to the farthest leaf node. 

Recursive Solution:
The idea is still using pre-order traversal. We keep tracking of the depth of each node, and if the node is a leaf, we compare its depth with the maximum depth, and update the maximum depth, if needed. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int max = Integer.MIN_VALUE;
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        
        maxDepth(root, 0);
        
        return max;
    }
    
    private void maxDepth(TreeNode root, int depth) {
        if (root == null) return;
        
        depth++;
        
        // check if it is leaf
        if (root.left == null && root.right == null && depth > max) {
            max = depth;
        }
        
        maxDepth(root.left, depth);
        maxDepth(root.right, depth);
    }
}

Post-order Traversal Solution:
So far I think we have been very familiar with the pre-order traversal. For this problem, we can also do the post-order traversal of the binary tree, i.e, traverse from bottom-up. 

Code (Java):
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        
        return Math.max(left, right) + 1;
    }
}


Post-order Iterative Solution:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        
        int max = 0;
        Stack<TreeNode> nodeStack = new Stack<TreeNode>();
        Stack<Integer> depthStack = new Stack<Integer>();
        
        TreeNode prev = null;
        nodeStack.push(root);
        depthStack.push(1);
        
        while (!nodeStack.empty()) {
            TreeNode curr = nodeStack.peek();
            int depth = depthStack.peek();
            
            if (prev == null || curr == prev.left || curr == prev.right) {
                if (curr.left != null) {
                    nodeStack.push(curr.left);
                    depthStack.push(depth + 1);
                } else if (curr.right != null) {
                    nodeStack.push(curr.right);
                    depthStack.push(depth + 1);
                } else {
                    nodeStack.pop();
                    depthStack.pop();
                    if (depth > max) max = depth;
                }
            } else if (prev == curr.left) {
                if (curr.right != null) {
                    nodeStack.push(curr.right);
                    depthStack.push(depth + 1);
                } else {
                    nodeStack.pop();
                    depthStack.pop();
                    if (depth > max) max = depth;
                }
            } else if (prev == curr.right) {
                nodeStack.pop();
                depthStack.pop();
                if (depth > max) max = depth;
            }
            
            prev = curr;
        }
        return max;
    }
}


Summary:
The problem itself is very simple, but do be familiar with the post-order traversal. Especially be careful about the return value and how it works. 

Update on 9/30/14:
DFS solution:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int max = 0;
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        maxDepthHelper(root, 0);
        
        return max;
    }
    
    private void maxDepthHelper(TreeNode root, int depth) {
        if (root == null) {
            return;
        }
        
        depth++;
        
        if (root.left == null && root.right == null) {
            if (depth > max) {
                max = depth;
            }
            return;
        }
        
        maxDepthHelper(root.left, depth);
        maxDepthHelper(root.right, depth);
    }
}

Divide-and-Conquer
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int maxDepth(TreeNode root) {
        
        if (root == null) {
            return 0;
        }
        
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        
        return Math.max(left, right) + 1;
    }
}

Leetcode: 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.

Understand the problem:
The problem asks for determining if a tree is height-balanced. Note that the height-balanced binary tree is defined as the depth of the two subtress of every node never differ by more than 1. 

Solution:
The basic idea of this problem is very similar to the path sum problem, which we can still employ the preorder traversal. The difference is we can maintain two variable, min and max, and update them when we reach to a leaf node. If we found that the difference between max and min is greater than one, it indicates that the tree is not height-balanced. 

Wrong Answer:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int min = Integer.MAX_VALUE;
    private int max = Integer.MIN_VALUE;
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        
        // the cornor case where the tree has no left or right subtrees
        if (root.left == null || root.right == null) {
            min = 1;
            max = 1;
        }
        return preOrderTraversal(root, 0);
    }
    
    private boolean preOrderTraversal(TreeNode root, int depth) {
        if (root == null) return true;
        
        depth++;
        // if it is leaf
        if (root.left == null && root.right == null) {
            if (depth < min) min = depth;
            if (depth > max) max = depth;
            
            if ((max - min) > 1) return false;
        }
        
        if (preOrderTraversal(root.left, depth) == false) return false;
        if (preOrderTraversal(root.right, depth) == false) return false;
        
        return true;
    }
}

Why the solution is wrong? Consider the following case:
                  1
                 /   \
               2    2
              /        \
            3          3
           /              \
          4               4 
For the solution above, it will falsely think of the tree is balanced. However is not. 
Let's go back to the definition of the balanced tree. The height of a binary tree is defined as the number of nodes from the current node to the deepest leaf. Note the difference between the depth and height of a tree. The depth of a tree is the number of nodes from root to the current node.  So for a height-balanced tree, for a node, both its left subtree and right subtree should be balanced. In the example above, the node 2 on the left is not height-balanced, since its left height is 3, while the right is 1. 

Correct Solution:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        
        if (getHeight(root) == -1) return false;
        
        return true;
    }
    
    private 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;
    }
}

So we can see that the correct solution used post-order traversal. We used post-order traversal because we got the height bottom-up. 


Update 9/30/14:
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        
        return isBalancedHelper(root) != -1;
    }
    
    private int isBalancedHelper(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        int left = isBalancedHelper(root.left);
        int right = isBalancedHelper(root.right);
        
        if (left == -1 || right == -1 || Math.abs(left - right) > 1) {
            return -1;
        }
        
        return Math.max(left, right) + 1;
    }
}

Update on 4/30/19:
/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int val) {
 *         this.val = val;
 *         this.left = this.right = null;
 *     }
 * }
 */

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) {
        // write your code here
        if (root == null) {
            return true;
        }
        
        ResultType ans = isBalancedHelper(root);
        
        return ans.balanced;
    }
    
    private ResultType isBalancedHelper(TreeNode root) {
        if (root == null) {
            return new ResultType(0, true);
        }
        
        ResultType left = isBalancedHelper(root.left);
        ResultType right = isBalancedHelper(root.right);
        
        int depth = Math.max(left.depth, right.depth) + 1;
        boolean balanced = left.balanced && right.balanced && Math.abs(left.depth - right.depth) <= 1;
        ResultType ans = new ResultType(depth, balanced);
        
        return ans;
    }
}

class ResultType {
    int depth;
    boolean balanced;
    
    public ResultType(int depth, boolean balanced) {
        this.depth = depth;
        this.balanced = balanced;
    }
}