Leetcode - Lowest Common Ancestor of a Binary Tree

Paste_Image.png

My code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private TreeNode ancestor = null;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null)
            return null;
        else if (p == q)
            return p;
        boolean[] ret = findAncestor(root, p, q);
        return ancestor;
    }
    
    private boolean[] findAncestor(TreeNode root, TreeNode p, TreeNode q) {
        boolean[] retLeft = null;
        boolean[] retRight = null;
        boolean[] ret = new boolean[3];
        
        if (root.left != null) {
            retLeft = findAncestor(root.left, p, q);
            if (retLeft != null) {
                ret[0] |= retLeft[0];
                ret[1] |= retLeft[1];
                ret[2] |= retLeft[2];
                if (ret[2])
                    return ret;
            }
        }
        
        if (root.right != null) {
            retRight = findAncestor(root.right, p, q);
            if (retRight != null) {
                ret[0] |= retRight[0];
                ret[1] |= retRight[1];
                ret[2] |= retRight[2];
                if (ret[2])
                    return ret;
            }
        }
        
        if (root == p)
            ret[0] = true;
        else if (root == q)
            ret[1] = true;
        
        if (ret[0] && ret[1]) {
            ret[2] = true;
            ancestor = root;
        }
        return ret;
    }
}

My test result:


Paste_Image.png

這道題目還是挺帶勁的。想了挺久。最后終于寫出來,感覺自己功力還是挺深厚的。

首先提醒。
**形參是無法改變實(shí)參的,即使傳入的是指針。除非是容器。比如這道題目中,
我一開始這么寫的函數(shù)。

private boolean[] findAncestor(TreeNode root, TreeNode p, TreeNode q, TreeNode ancestor) {....}

然后在主函數(shù)中傳入ancestor,妄想著在遞歸中,給他賦值,然后改變實(shí)參。也就是主函數(shù)中的ancestor。但是,是無法改變的。遞歸的時候,只是把這個指針的地址壓入棧中。如果這個指針取得了新的地址,那么在這段棧里面,他的值會被改變成另外一個地址值。但是這個子函數(shù)并沒有返回這個地址,那么,當(dāng)這個子函數(shù)結(jié)束時,這段地址就被拋棄了。就算他改變了,也不能使上一層的實(shí)參指針的地址值發(fā)生改變。
什么情況下會改變呢?當(dāng)實(shí)參是容器時,那么傳入的容器增減,是可以反映到實(shí)參的。
所以,permutation那類題,不能拷貝地址值給arraylist而必須新建一個arraylist拷貝所有元素,然后再添加進(jìn)大arraylist。同樣的,需要不斷地remove。
**

這道題目是什么思想呢。
我返回一個三個布爾量的數(shù)組。他們的意義是。
boolean[] ret
ret[0] p is find -> true
ret[1] q is find -> true
ret[2] pq is find -> ret[0] & ret[1] == true -> true

然后是用post-order 來遍歷的。
因?yàn)槲野l(fā)現(xiàn), pre-order都是先遍歷頭結(jié)點(diǎn),然后再左再右。
而 post-order 是先遍歷子節(jié)點(diǎn),左右,在往上。
所以,當(dāng)我到達(dá)root時,
我先pre order 左子樹,
然后看下這個返回的布爾數(shù)組,ret[2] 是否為真,如果為真,那么就是已經(jīng)找到了,我就不需要再找了,直接返回。
如果返回的布爾數(shù)組不是空的,但ret[2] false。
那么,我就將 ret[0], ret[1] 與這個布爾數(shù)組對應(yīng)為進(jìn)行或操作。只要pq有一個被找到,就可以通過或操作反映到ret上。
對右子樹進(jìn)行同樣的操作。
然后再判斷root自己。是否是pq
再判斷下ret[0], ret[1] 是否都為真,如果是的,就代表都找到了,且祖先就是root。
ret[2] 設(shè)置為true并返回。
否則就直接返回。

同時,我一開始的代碼不是這樣的。因?yàn)樽雍瘮?shù)可能返回null,為了避免bug,我就一開始給左右布爾數(shù)組都申請了內(nèi)存。但是想到今早看書,老教授的一句話。
在遞歸函數(shù)中申請內(nèi)存是一件十分消耗資源的事,能不做就盡量不做。所以我最后找了個辦法避免了。多寫了幾個if語句,所以運(yùn)行時間可能要慢一點(diǎn),但是一定省掉了許多內(nèi)存。

**
總結(jié): post-order tree
**

Anyway, Good luck, Richardo!

My code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        else if (root == p || root == q) {
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left == null && right == null) {
            return null;
        }
        else if (left == null || right == null) {
            return left == null ? right : left;
        }
        else {
            return root;
        }
    }
}

同學(xué)提醒后做了出來。是一個dfs
bottom up
post-order
如果root == p or q,就返回p or q
否則,繼續(xù)dfs,再判斷返回的左右是否都是空,如果是,就代表p,q不在這個sub tree里面,就返回null
如果有一個為空,則另一個可能是找到的p or q,也可能就是他們的ancestor,直接往上層返回就行。
如果左右都不是空,那么當(dāng)前root就是ancestor,直接返回root.

思路就是這樣的了。

Anyway, Good luck, Richardo! -- 09/05/2016

My code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        HashMap<TreeNode, TreeNode> map = new HashMap<TreeNode, TreeNode>();
        Stack<TreeNode> st = new Stack<TreeNode>();
        map.put(root, null);
        st.push(root);
        while (!map.containsKey(p) || !map.containsKey(q)) {
            TreeNode curr = st.pop();
            if (curr.left != null) {
                map.put(curr.left, curr);
                st.push(curr.left);
            }
            if (curr.right != null) {
                map.put(curr.right, curr);
                st.push(curr.right);
            }
        }
        
        HashSet<TreeNode> set = new HashSet<TreeNode>();
        while (p != null) {
            set.add(p);
            p = map.get(p);
        }
        
        while (q != null) {
            if (set.contains(q)) {
                return q;
            }
            set.add(q);
            q = map.get(q);
        }
        
        return null;
    }
}

reference:
https://discuss.leetcode.com/topic/27479/java-python-iterative-solution/2

這才是真正優(yōu)美的解決方法。
利用一個map保持父子關(guān)系。如果 union find 一般。
然后,利用它,我們還可以找到 p and q 的路徑。

Tree + HashMap, 這是一種全新的組合,第一次接觸。

Anyway, Good luck, Richardo! -- 09/24/2016

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,776評論 0 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,981評論 19 139
  • Description: Given a binary tree, find the lowest common ...
    黑山老水閱讀 605評論 0 0
  • 實(shí)際上,不得不承認(rèn):心里總是矛盾的,一方面希望兒子快快長大;一方面又覺得等孩子長大了,我就老得老不得了! 今天毛毛...
    萬春英閱讀 968評論 1 0
  • 材質(zhì)包含如下幾種類型,每一種是一個獨(dú)立的插件。 carpaint(車漆) glass(玻璃) glossy(有光澤...
    _尸兄閱讀 1,211評論 0 0