劍指offer系列-面試題-面試題68 - II. 二叉樹的最近公共祖先 (python)

1. 題目

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

百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”

2. 解題思路

詳情見 面試題68 - II. 二叉樹的最近公共祖先(後序遍歷 DFS ,清晰圖解)

關鍵點是二叉搜索樹,研究發現有如下規律。

三種情況:

1)p, q 一個在左子樹,一個在右子樹,那麼當前節點就是最近公共祖先
2)p, q 都在左子樹
3) p, q 都在右子樹

3. 代碼實現

3.0 路徑比較

這個是我自己想到的,看了leetcode的題解之後,感覺自己就是個渣渣。o(╥﹏╥)o

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        """
        1. 記錄找到p和q的路徑
        2. 從找到p和q的第一個公共祖先
        """
        def helper(cur, p):
            """
            返回p在cur樹中的路徑,路徑肯定是唯一的
            """
            path = [None] # 路徑
            stack = [(None, cur)]

            while cur:
                father, cur = stack.pop()
                while path[-1] != father: path.pop()
                
                path.append(cur)
                if p.val == cur.val: break # 找到p了
                if cur.right: stack.append((cur, cur.right))
                if cur.left: stack.append((cur, cur.left))

                if not (cur.right or cur.left): path.pop() 

            return path[:0:-1]
        
        ps = helper(root, p)
        qs = helper(root, q)

        for i in ps: # 找到最近公共祖先
            for j in qs:
                if i == j:
                    return j

        return 

3.1 遞歸

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left: return right
        if not right: return left
        return root     

    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        """
        思路:
        三種情況:
        1)p, q 一個在左子樹,一個在右子樹,那麼當前節點就是最近公共祖先
        2)p, q 都在左子樹
        3) p, q 都在右子樹

        所以,若返回值中l和r都是節點,表明左右子樹中一邊一個節點
            若返回值中l和r只有一個是節點,表明兩個節點都在某一側的子樹中
        """
        if not root or root == p or root == q:
            return root
        
        l = self.lowestCommonAncestor(root.left, p, q) # 左子樹中 p, q 的LCA,返回值爲None或節點
        r = self.lowestCommonAncestor(root.right, p, q) # 右子樹中 p, q 的LCA,返回值爲None或節點
        
        if l and r:
            return root
        return l or r # 短路原則。

4. 總結

關鍵能夠發現:什麼條件下這個點是LCA(Least Common Ancestors )。

5. 參考文獻

[1] 劍指offer叢書
[2] 劍指Offer——名企面試官精講典型編程題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章