剑指offer系列-面试题-面试题68 - II. 二叉树的最近公共祖先 (python)

文章目录

  • 1. 题目
  • 2. 解题思路
  • 3. 代码实现
    • 3.0 路径比较
    • 3.1 递归
  • 4. 总结
  • 5. 参考文献

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——名企面试官精讲典型编程题

你可能感兴趣的:(算法)