1. 程式人生 > 其它 >LeetCode-二叉樹的最近公共祖先

LeetCode-二叉樹的最近公共祖先

二叉樹的最近公共祖先

給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。

遇到任何遞迴型的問題,無非就是靈魂三問

1、這個函式是幹嘛的

情況 1,如果pq都在以root為根的樹中,函式返回的即使pq的最近公共祖先節點。

情況 2,那如果pq都不在以root為根的樹中怎麼辦呢?函式理所當然地返回null唄。

情況 3,那如果pq只有一個存在於root為根的樹中呢?函式就會返回那個節點。

2、這個函式引數中的變數是什麼的是什麼

函式引數中的變數是root,因為根據框架,lowestCommonAncestor(root)會遞迴呼叫root.leftroot.right

;至於pq,我們要求它倆的公共祖先,它倆肯定不會變化的。

把「以root為根」轉移成「以root的子節點為根」,不斷縮小問題規模

3、得到函式的遞迴結果,你應該幹什麼

先想 base case,如果root為空,肯定得返回null。如果root本身就是p或者q,比如說root就是p節點吧,如果q存在於以root為根的樹中,顯然root就是最近公共祖先;即使q不存在於以root為根的樹中,按照情況 3 的定義,也應該返回root節點。

以上兩種情況的 base case 就可以把框架程式碼填充一點了:

TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    // 兩種情況的 base case
    if (root == null) return null;
    if (root == p || root == q) return root;

    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
}

用遞迴呼叫的結果leftright來搞點事情。根據剛才第一個問題中對函式的定義,我們繼續分情況討論:

情況 1,如果pq都在以root為根的樹中,那麼leftright一定分別是pq(從 base case 看出來的)。

情況 2,如果pq都不在以root為根的樹中,直接返回null

情況 3,如果pq只有一個存在於root為根的樹中,函式返回該節點。

leftright非空,分別是pq,可以說明root是它們的公共祖先,但能確定root就是「最近」公共祖先嗎?

這就是一個巧妙的地方了,因為這裡是二叉樹的後序遍歷啊!前序遍歷可以理解為是從上往下,而後序遍歷是從下往上,就好比從p

q出發往上走,第一次相交的節點就是這個root,所以這個root當然是最近公共祖先了

public class LowestCommonAncestor {

    public class TreeNode {
      int val;
      TreeNode left;
      TreeNode right;
      TreeNode(int x) { val = x; }
    }

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // base case
        if(root == null) {
            return null;
        }
        // 如果遍歷到的根節點為p或者q,說明在父節點的子樹下找到了該節點,所以返回該父節點
        if(p==root||q==root) {
            return root;
        }
        // 後序遍歷,遞迴呼叫,left是在父節點的左子樹中找到的p節點或者q節點或者空節點,right同理
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        // 如果左子樹和右子樹都找到了p,q節點,說明父節點就是最近公共祖先,因為是後序遍歷,所以能確保該父節點就是最近的祖先
        if(left!=null&&right!=null) {
            return root;
        }
        // 如果左子樹和右子樹都沒有找到p,q節點,說明p,q節點和該父節點沒關係,返回null,表示該父節點的子樹下不包含p,q節點
        if(left==null&&right==null) {
            return null;
        }
        // 在上邊條件都不滿足的情況下,說明在該父節點下只找到了p或者q其中一個節點,返回這個找到的節點即可,
        // 如果是最外層的遞迴呼叫可能是因為另一個節點在這個節點的子樹下,已經遍歷找到了這個節點,就直接返回了沒有再去找另一個節點,或者是兩個節點都在左子樹的某一個父節點下,所以右子樹沒有找到節點,這裡當然返回這兩個節點所在樹的子呼叫裡的父節點也就是現在返回的left了,右子樹同理
        return left!=null?left:right;
    }
}