Friday, June 29, 2018

394. Decode String

394Decode String
Given an encoded string, return it's decoded string.
The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.
You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc.
Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 2[4].
Examples:
s = "3[a]2[bc]", return "aaabcbc".
s = "3[a2[c]]", return "accaccacc".
s = "2[abc]3[cd]ef", return "abcabccdcdcdef".

--------------------------------
恶心的题。
思路:处理String分2种情况 1- 是数字 2- 是字母。
数字的话先取得数字,然后对括号里的字母继续进行读取,如果又遇到数字就递归
字母就没啥好说的,吃了就是

ToDo, 用stack再写一次

class Solution {
    public String decodeString(String s) {
        String rt = "";
        int i = 0;
        
        while (i < s.length()) {
            Pair p = nextWord(s, i);
            i = p.i;
            rt += p.s;
        }
            
        return rt;
    }
    
    private Pair getDigits(String s, int i) {
        String d = "";
        while (Character.isDigit(s.charAt(i))) {
            d += s.charAt(i);
            i++;
        }
        i++;
        
        return new Pair(d, i);
    }
    
    private Pair getWord(String s, int i) {
        String st = "";
        while (i < s.length() && s.charAt(i) != ']') {
            if (Character.isDigit(s.charAt(i))) {
                Pair p = nextWord(s, i);
                st += p.s;
                i = p.i;
            } else {
                st += s.charAt(i);
                i++;
            }
        }
        i++;
        
        return new Pair(st, i);
    }
    
    private Pair getSimpleWord(String s, int i) {
        String st = "";
        while (i < s.length() && !Character.isDigit(s.charAt(i)) && s.charAt(i) != ']') {
            st += s.charAt(i);
            i++;
        }

        return new Pair(st, i);
    }
    
    private Pair nextWord(String s, int i) {
        
        if (Character.isDigit(s.charAt(i))) {
        
            Pair digits = getDigits(s, i);
            i = digits.i;
            
            Pair word = getWord(s, i);
            String st = word.s;
            
            int n = Integer.parseInt(digits.s);
            String rt = "";
            for (int j = 0; j < n; j++) {
                rt += st;
            }
            
            return new Pair(rt, word.i);
            
        }else {
            return getSimpleWord(s, i);
        }
    }
}
            
class Pair{
    public String s;
    public int i;
    public Pair(String s, int i) {
        this.s = s;
        this.i = i;
    }
}

Updated on Dec-23rd-2018
跟上面思路一样,区别在于遇到letter的时候就直接加。这样写起来更简单
class Solution {
    private int i = 0;
    public String decodeString(String s) {
        return dfs(s);
    }
    
    private int getDigit(String s) {
        int rt = 0;
        
        while (Character.isDigit(s.charAt(i))) {
            rt = rt * 10 + (s.charAt(i) - '0');
            i++;
        }
        return rt;
    }
    
    private String dfs(String s) {
        StringBuilder sb = new StringBuilder();
        
        while (i < s.length() && s.charAt(i) != ']') {
            char c = s.charAt(i);
            if (Character.isDigit(c)) {
                int times = getDigit(s);
                
                i++; // '['
                String word = dfs(s);
                
                while (times > 0) {
                    sb.append(word);
                    times--;
                }
                
            }else {
                sb.append(c);        
            }
            i++;
        }
        
        return sb.toString();
    }
}

Thursday, June 28, 2018

721 Accounts Merge

721Accounts Merge
Given a list accounts, each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.
Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some email that is common to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.
After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.
Example 1:
Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
The first and third John's are the same person as they have the common email "johnsmith@mail.com".
The second John and Mary are different people as none of their email addresses are used by other accounts.
We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], 
['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted.
Note:




  • The length of accounts will be in the range [1, 1000].
  • The length of accounts[i] will be in the range [1, 10].
  • The length of accounts[i][j] will be in the range [1, 30].
  • ---------------------------------
    Union Find
    最后返回的时候繁琐了一点,因为题目要求sort过,跟名字一定要在第一位。
    Todo:分析复杂度
    class Solution {
        public List<List<String>> accountsMerge(List<List<String>> accounts) {
            Map<String, Integer> m = new HashMap<>();
            int n = accounts.size();
            int[] uf = getUF(n);
            
            for (int i = 0; i < accounts.size(); i++) {
    
                List<String> row = accounts.get(i);
                for (int j = 1; j < row.size(); j++) {
                    String email = row.get(j);
                    
                    if (m.containsKey(email) && getRoot(uf, m.get(email)) != getRoot(uf, i)) {
                        int index = m.get(email);
                        uf[getRoot(uf, uf[i])] = getRoot(uf, index);
                        n--;
                    }else {
                        m.put(email, getRoot(uf, i));
                    }
                }
            }
            
            return sortAndReturn(m, uf);
        }
        
        private List<List<String>> sortAndReturn(Map<String, Integer> m, int[] uf) {
            Map<Integer,List<String>> rt = new HashMap<>();
            for (Map.Entry<String, Integer> entry : m.entrySet()) {
                int index = getRoot(uf, entry.getValue());
                if (!rt.containsKey(index)) {
                    rt.put(index, new ArrayList<String>());    
                }
                rt.get(index).add(entry.getKey());
            }
            
            List<List<String>> ret= new ArrayList<>();
            for (List<String> l : rt.values()) {
                List<String> temp = new ArrayList<>(l);
                Collections.sort(temp);
                String name = accounts.get(getRoot(uf, m.get(temp.get(0)))).get(0);
                temp.add(0, name);
                ret.add(temp);
            }
            
            return ret;
        }
        
        private int getRoot(int[] uf, int index) {
            
            while (uf[index] != index) {
                index = uf[index];
                uf[index] = uf[uf[index]];
            }
            
            return index;
        }
        
        private int[] getUF(int n) {
            int[] rt = new int[n];
            for (int i = 0; i < n; i++) {
                rt[i] = i;
            }
            
            return rt;
        }
    }
    

    ToDo, DFS做法,见https://leetcode.com/problems/accounts-merge/solution/

    Friday, June 22, 2018

    341. Flatten Nested List Iterator

    341Flatten Nested List Iterator
    Given a nested list of integers, implement an iterator to flatten it.
    Each element is either an integer, or a list -- whose elements may also be integers or other lists.
    Example 1:
    Given the list [[1,1],2,[1,1]],
    By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1].
    Example 2:
    Given the list [1,[4,[6]]],
    By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6].
    -------------------------------
    一种说法认为不应该把给出的list转换并存在本地,应该直接在上面遍历
    阅读:Java的Iterator class和ListIterator class

    没什么可说的
    /**
     * // This is the interface that allows for creating nested lists.
     * // You should not implement it, or speculate about its implementation
     * public interface NestedInteger {
     *
     *     // @return true if this NestedInteger holds a single integer, rather than a nested list.
     *     public boolean isInteger();
     *
     *     // @return the single integer that this NestedInteger holds, if it holds a single integer
     *     // Return null if this NestedInteger holds a nested list
     *     public Integer getInteger();
     *
     *     // @return the nested list that this NestedInteger holds, if it holds a nested list
     *     // Return null if this NestedInteger holds a single integer
     *     public List<NestedInteger> getList();
     * }
     */
    public class NestedIterator implements Iterator<Integer> {
    
        private int index;
        private List<Integer> list;
        public NestedIterator(List<NestedInteger> nestedList) {
            list = getList(nestedList);
            index = 0;
        }
        
        private List<Integer> getList(List<NestedInteger> nestedList) {
            List<Integer> l = new ArrayList<>();
            for (NestedInteger n : nestedList) {
                if (n.isInteger()) {
                    l.add(n.getInteger());
                }else {
                    l.addAll(getList(n.getList()));
                }
            }
            
            return l;
        }
    
        @Override
        public Integer next() {
            int save = list.get(index);
            index++;
            return save;
        }
    
        @Override
        public boolean hasNext() {
            return index < list.size();
        }
    }
    
    /**
     * Your NestedIterator object will be instantiated and called as such:
     * NestedIterator i = new NestedIterator(nestedList);
     * while (i.hasNext()) v[f()] = i.next();
     */
    

    Solution #2, 用stack
    ToDo: https://leetcode.com/problems/flatten-nested-list-iterator/discuss/80147/Simple-Java-solution-using-a-stack-with-explanation

    Thursday, June 21, 2018

    282 Expression Add Operators

    282Expression Add Operators
    Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not unary) +-, or *between the digits so they evaluate to the target value.
    Example 1:
    Input: num = "123", target = 6
    Output: ["1+2+3", "1*2*3"] 
    
    Example 2:
    Input: num = "232", target = 8
    Output: ["2*3+2", "2+3*2"]
    Example 3:
    Input: num = "105", target = 5
    Output: ["1*0+5","10-5"]
    Example 4:
    Input: num = "00", target = 0
    Output: ["0+0", "0-0", "0*0"]
    
    Example 5:
    Input: num = "3456237490", target = 9191
    Output: []
    

    -------------------------
    典型的dfs。
    唯一的难点在“巧妙”地处理参数cut, 可以运用到arithmetic evaluation题型
    credit: https://leetcode.com/problems/expression-add-operators/discuss/71895/Java-Standard-Backtrace-AC-Solutoin-short-and-clear
    class Solution {
        public List addOperators(String num, int target) {
            
            List rt = new ArrayList<>();
            if (num == null || num.length() == 0) return rt;
            
            dfs(rt, num, 0, "", 0, 0, target);
            
            return rt;
        }
        
        private void dfs(List rt, String num, int index, String sofar, long evl, long cut, int target) {
            if (index == num.length() && target == evl) {
                
                rt.add(sofar);    
                return;
            }
            
            for (int i = index; i < num.length(); i++) {
                if (i != index && num.charAt(index) == '0') return;
                String s = num.substring(index,i + 1);
                long val = Long.parseLong(s);
                
                if (index == 0) {
                    dfs(rt, num, i + 1, s, val, val, target);    
                }else {
                    dfs(rt, num, i + 1, sofar + "+" + s, evl + val, val, target);
                    dfs(rt, num, i + 1, sofar + "-" + s, evl - val, -val, target);
                    dfs(rt, num, i + 1, sofar + "*" + s, evl - cut + cut * val, cut * val, target);
                }
            }
        }
    }
    

    Wednesday, June 20, 2018

    636 Exclusive Time of Functions

    636Exclusive Time of Functions
    Given the running logs of n functions that are executed in a nonpreemptive single threaded CPU, find the exclusive time of these functions.
    Each function has a unique id, start from 0 to n-1. A function may be called recursively or by another function.
    A log is a string has this format : function_id:start_or_end:timestamp. For example, "0:start:0" means function 0 starts from the very beginning of time 0. "0:end:0" means function 0 ends to the very end of time 0.
    Exclusive time of a function is defined as the time spent within this function, the time spent by calling other functions should not be considered as this function's exclusive time. You should return the exclusive time of each function sorted by their function id.
    Example 1:
    Input:
    n = 2
    logs = 
    ["0:start:0",
     "1:start:2",
     "1:end:5",
     "0:end:6"]
    Output:[3, 4]
    Explanation:
    Function 0 starts at time 0, then it executes 2 units of time and reaches the end of time 1. 
    Now function 0 calls function 1, function 1 starts at time 2, executes 4 units of time and end at time 5.
    Function 0 is running again at time 6, and also end at the time 6, thus executes 1 unit of time. 
    So function 0 totally execute 2 + 1 = 3 units of time, and function 1 totally execute 4 units of time.
    
    Note:
    1. Input logs will be sorted by timestamp, NOT log id.
    2. Your output should be sorted by function id, which means the 0th element of your output corresponds to the exclusive time of function 0.
    3. Two functions won't start or end at the same time.
    4. Functions could be called recursively, and will always end.
    5. 1 <= n <= 100
    -------------------------
    题意模糊的烂题
    用stack来做, 遇到start入栈,end出栈。2个操作都要累计当前进程获得cpu的时间。 用pre来记录上一次操作的时间点
    注意logs是按时间排序。#20,#21 两处+1
    class Solution {
        public int[] exclusiveTime(int n, List logs) {
            int[] times = new int[n];
            Stack st = new Stack<>();
            int pre = 0;
            
            int preTime = 0;
            for (String log : logs) {
                String[] a = log.split(":");
                int id = Integer.parseInt(a[0]);
                int cur = Integer.parseInt(a[2]);
                if (a[1].equals("start")) {
                    if (!st.isEmpty()) {
                        times[st.peek()] += cur - pre;
                    }
                    pre = cur;
                    st.push(id);
                }else {
                    st.pop();
                    times[id] += cur - pre + 1;
                    pre = cur + 1;
                }
            }
            
            return times;
        }
    }
    

    Thursday, June 14, 2018

    680 Valid Palindrome II

    680Valid Palindrome II
    Given a non-empty string s, you may delete at most one character. Judge whether you can make it a palindrome.
    Example 1:
    Input: "aba"
    Output: True
    
    Example 2:
    Input: "abca"
    Output: True
    Explanation: You could delete the character 'c'.
    
    Note:
    1. The string will only contain lowercase characters a-z. The maximum length of the string is 50000
    ------------------------
    没什么好说的

    class Solution {
        public boolean validPalindrome(String s) {
            int i = 0, j = s.length() - 1;
            boolean flag = false;
            
            while (i < j) {
                if (s.charAt(i) != s.charAt(j)) {
                    return furtherPalindrome(s, i + 1, j) || furtherPalindrome(s, i, j - 1);
                }
                i++;
                j--;
            }
            
            return true;
        }
        
        private boolean furtherPalindrome(String s, int i, int j) {
            while (i < j) {
                if (s.charAt(i) != s.charAt(j)) return false;
                i++;
                j--;
            }
            
            return true;
        }
    }
    

    ToDo

    Reading:
    BIT: https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/
    Binary Search: https://www.topcoder.com/community/competitive-programming/tutorials/binary-search/
    --------

    Word Break II, java遍历写一遍
    ------------------
    Subset 所有解法再过一次 http://shibaili.blogspot.com/2013/06/day-38-77-combinations.html

    ----------------
    721 Accounts Merge 用DFS写一遍: http://shibaili.blogspot.com/2018/06/721-accounts-merge.html

    ----------
    394. Decode String stack 再写一次

    ------
    Done
    218. The Skyline Problem 再写一次

    http://shibaili.blogspot.com/2015/06/day-110-skyline-problem.html
    ref: https://briangordon.github.io/2014/08/the-skyline-problem.html

    ------------
    再实现一次heap

    ------
    写一遍:https://leetcode.com/problems/longest-palindromic-subsequence/description/

    ----
    wildcard matching,regular expression再写一次

    ------
    Done
    Word Ladder II 再写一次
    -------
    Valid number
    regex,state machine跟普通方法都写一次

    -------
    772. Basic Calculator III
    http://shibaili.blogspot.com/2018/08/772-basic-calculator-iii.html 用通解把I,II再写一次
    ------
    269. Alien Dictionary
    代码精简一下。
    bfs写一次
    http://shibaili.blogspot.com/2018/08/269-alien-dictionary.html

    -------
    403. Frog Jump
    2d-array写一次

    ------
    再写一次同一类型的题
    reverse pair
    Count of Smaller Numbers After Self
    Count of Range Sum
    ---------

    改进UnionFind
    399. Evaluate Division

    ------
    心情好的时候把这个写一下:
    308. Range Sum Query 2D - Mutable
    https://leetcode.com/problems/range-sum-query-2d-mutable/description/

    ------
    再写一次
    449. Serialize and Deserialize BST

    -------
    Greedy, 贪心算法能保证最优解吗?
    https://leetcode.com/problems/course-schedule-iii/description/

    ------------
    BFS
    http://shibaili.blogspot.com/2018/10/329-longest-increasing-path-in-matrix.html

    ----
    二分法的一种写法是不设base case,然后由while里的条件来结束,最后返回left
    https://shibaili.blogspot.com/2015/01/day-94-find-peak-element.html

    ------------------
    465. Optimal Account Balancing

    -----

    818. Race Car
    DP的iterative写法

    -----------------
    753. Cracking the Safe
    为什么for loop里面顺序要倒着

    Sunday, June 10, 2018

    689 Maximum Sum of 3 Non-Overlapping Subarrays

    689Maximum Sum of 3 Non-Overlapping Subarrays
    In a given array nums of positive integers, find three non-overlapping subarrays with maximum sum.
    Each subarray will be of size k, and we want to maximize the sum of all 3*k entries.
    Return the result as a list of indices representing the starting position of each interval (0-indexed). If there are multiple answers, return the lexicographically smallest one.
    Example:
    Input: [1,2,1,2,6,7,5,1], 2
    Output: [0, 3, 5]
    Explanation: Subarrays [1, 2], [2, 6], [7, 5] correspond to the starting indices [0, 3, 5].
    We could have also taken [2, 1], but an answer of [1, 3, 5] would be lexicographically larger.
    
    Note:

  • nums.length will be between 1 and 20000.
  • nums[i] will be between 1 and 65535.
  • k will be between 1 and floor(nums.length / 3).
  • --------------------------- 
    算法:假设i为要求的第2个subarray的起始点,则在[0, i - 1]和[i + k, end]各存在一个subarray,找出这3个subaaray和的最大值时的坐标就可

    恶心的实现题,注意各种index的计算,最好用一个具体例子。

    计算subarray的题一般都要用到累加数组,这题也不例外。

    class Solution {
        public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
            int n = nums.length;
            int[] rt = new int[3], sums = new int[n + 1], leftMax = new int[n], rightMax = new int[n];
            // 计算累加数组
            for (int i = 0; i < n; i++) {
                sums[i + 1] = sums[i] + nums[i];
            }
            
            // 计算i左边的最大subarray和,DP
            for (int i = 1; i < n - 3 * k + 1; i++) {
                int cur = sums[i + k ] - sums[i];
                int preMax = sums[leftMax[i - 1] + k] - sums[leftMax[i - 1]];
                if (cur > preMax) {
                    leftMax[i] = i;
                }else {
                    leftMax[i] = leftMax[i - 1];
                }
            }
    
            // 计算i右边的最大subarray和,DP
            rightMax[n - k] = n - k;
            for (int i = n - k - 1; i >= 2 * k; i--) {
                int cur = sums[i + k] - sums[i];
                int preMax = sums[rightMax[i + 1] + k] - sums[rightMax[i + 1]];
                if (cur > preMax) {
                    rightMax[i] = i;
                }else {
                    rightMax[i] = rightMax[i + 1];
                }
            }
    
            int max = 0;
            for (int i = k; i <= n - 2 * k; i++) {
                int left = leftMax[i - k];
                int right = rightMax[i + k];
                int maxLeft = sums[left + k] - sums[left];
                int maxRight = sums[right + k] - sums[right];
                int cur = sums[i + k] - sums[i];
                int total = maxLeft + maxRight + cur;
                if (total > max) {
                    rt[0] = left;
                    rt[1] = i;
                    rt[2] = right;
                    max = total;
                }
            }
    
            return rt;
        }
    }
    

    Sunday, June 3, 2018

    311, 543 Sparse Matrix Multiplication, Diameter of Binary Tree

    311Sparse Matrix Multiplication
    Given two sparse matrices A and B, return the result of AB.
    You may assume that A's column number is equal to B's row number.
    Example:
    Input:
    
    A = [
      [ 1, 0, 0],
      [-1, 0, 3]
    ]
    
    B = [
      [ 7, 0, 0 ],
      [ 0, 0, 0 ],
      [ 0, 0, 1 ]
    ]
    
    Output:
    
         |  1 0 0 |   | 7 0 0 |   |  7 0 0 |
    AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 |
                      | 0 0 1 |
    

    -----------------------
    Solution #1 Brute force, 正常的矩阵乘法,每一次行列相乘会得到[i][j]位置的最终结果
    复杂度:rowA * colA * colB
    或者:A矩阵的元素的个数 * colB
    class Solution {
        public int[][] multiply(int[][] A, int[][] B) {
            int rowA = A.length;
            int colB = B[0].length;
            int[][] rt = new int[rowA][colB];
            
            for (int row = 0; row < rowA; row++) {
                for (int col = 0; col < colB; col++) {
                    int s = 0;
                    for (int i = 0; i < B.length; i++) {
                        s += A[row][i] * B[i][col];
                    }
                    rt[row][col] = s;
                }
            }
            
            return rt;
        }
    }
    
    Solution #2 对矩阵A进行遍历,A中的每一个元素A[i][j]都会被调用到B[0].length次,并计入到结果矩阵。如果A[i][j] == 0,则可省去B[0].length次运算
    此算法不同处是每一次相乘相加得到的只是中间值,最终结果得等程序跑完之后才能算出

    复杂度: A矩阵里非零元素的个数 * colB
    class Solution {
        public int[][] multiply(int[][] A, int[][] B) {
            int rowA = A.length;
            int colA = A[0].length;
            int colB = B[0].length;
            int[][] rt = new int[rowA][colB];
            
            for (int i = 0; i < rowA; i++) {
                for (int j = 0; j < colA; j++) {
                    if (A[i][j] != 0) {
                        for (int k = 0; k < colB; k++) {
                            rt[i][k] += A[i][j] * B[j][k];
                        }
                    }
                }
            }
            
            return rt;
        }
    }
    
    稀疏矩阵压缩后再进行乘积。压缩的方法是保证行数不变,只存下非零的列数跟数值。
    http://www.cs.cmu.edu/~scandal/cacm/node9.html

    此方法对这题而言不是什么好解法,因为题目已经给定了2个非压缩的2维矩阵
    class Solution {
        public int[][] multiply(int[][] A, int[][] B) {
            int rowA = A.length;
            int colA = A[0].length;
            int colB = B[0].length;
            int[][] rt = new int[rowA][colB];
            
            List<List<Integer>> sA = getCompressed(A);
            List<List<Integer>> sB = getCompressed(B);
        
            for (int i = 0; i < rowA; i++) {
                for (int j = 0; j < sA.get(i).size(); j += 2) {                
                    int colAA = sA.get(i).get(j);
                    int valAA = sA.get(i).get(j + 1);    
                    
                    for (int k = 0; k < sB.get(colAA).size(); k += 2) {
                        int colBB = sB.get(colAA).get(k);
                        int valBB = sB.get(colAA).get(k + 1);    
                        rt[i][colBB] += valAA * valBB;
                    }
                    
                }
            }
            
            return rt;
        }
        
        private List<List<Integer>> getCompressed(int[][] matrix) {
            List<List<Integer>> s = new ArrayList<>();
            for (int i = 0; i < matrix.length; i++) {
                List<Integer> r = new ArrayList<>();
                for (int j = 0; j < matrix[0].length; j++) {
                    if (matrix[i][j] != 0) {
                        r.add(j);
                        r.add(matrix[i][j]);
                    }
                }
                s.add(r);
            }
            return s;
        }
    }
    

    543Diameter of Binary Tree
    Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.
    Example:
    Given a binary tree 
              1
             / \
            2   3
           / \     
          4   5    
    
    Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].
    Note: The length of path between two nodes is represented by the number of edges between them.


    ----------------------------
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        private int max = 0;
        public int diameterOfBinaryTree(TreeNode root) {
            rec(root);
            return max;
        }
        
        private int rec(TreeNode root) {
            if (root == null) return 0;
            int left = rec(root.left);
            int right = rec(root.right);
            
            max = Math.max(max, left + right);
            return Math.max(left, right) + 1;
        }
    }