LintCode 578. 最近公共祖先 III

2020-06-19  本文已影响0人  CW不要无聊的风格

题目描述

给一棵二叉树和二叉树中的两个节点,找到这两个节点的最近公共祖先LCA。

两个节点的最近公共祖先,是指两个节点的所有父亲节点中(包括这两个节点),离这两个节点最近的公共的节点。

返回 null 如果两个节点在这棵树上不存在最近公共祖先的话。

这两个节点未必都在这棵树上出现。

每个节点的值都不同


测试样例

输入:

{4, 3, 7, #, #, 5, 6}

3 5

5 6

6 7

5 8

输出:

4

7

7

null

解释:

  4

/ \

3  7

  / \

  5  6

LCA(3, 5) = 4

LCA(5, 6) = 7

LCA(6, 7) = 7

LCA(5, 8) = null

输入:

{1}

1 1

输出:

1

说明:

这棵树只有一个值为1的节点


解题思路

1、分冶

分别从左、右子树去寻找A、B与LCA, 每棵子树返回三个结果,分别代表是否发现了A、B以及找到的LCA,记左、右子树的返回结果分别为lA、lB、l_LCA 和 rA、rB、r_LCA;

得到左、右子树的处理结果后,若A、B是在不同子树下发现的,则当前节点必定是LCA,直接返回;

否则,若有其中一颗子树发现了A(B)或者当前节点即A(B),则代表发现了A(B),同时,将LCA设为左子树或右子树返回的LCA;进一步判断,若在当前节点下A、B都被发现了,且当前节点是A或B,则将LCA设为当前节点。

2、DFS then Common Route

使用DFS做遍历,在遍历过程中记录A、B的公共路径,每遍历到一个节点就将其加入到公共路径中;

若遍历到A/B,则设置A/B的路径为当前的公共路径;

若A和B的路径都已设置完毕,则无需再遍历;

每次DFS的最后,要判断下A和B的路径是否同时为空,若是,则需要将此次加入的节点从公共路径中删除,因为其不在A和B的公共路径中。

3、设置父节点,比较公共祖先

为每个节点设置父节点属性,然后判断下A、B是否存在该属性,若有其一不存在父节点属性,则说明其不在这棵树中,直接返回;

否则,从A开始递归,将其所有祖先加入到列表中(包括自己);

然后从B开始递归至其所有祖先,若递归到某个节点出现在A的祖先列表中,则代表其是LCA,直接返回;

最后,若以上步骤未求得接,返回None。


代码

1、分冶

"""

Definition of TreeNode:

class TreeNode:

    def __init__(self, val):

        this.val = val

        this.left, this.right = None, None

"""

class Solution:

    """

    @param: root: The root of the binary tree.

    @param: A: A TreeNode

    @param: B: A TreeNode

    @return: Return the LCA of the two nodes.

    """

    def lowestCommonAncestor3(self, root, A, B):

        _, _, LCA = self.helper(root, A, B)

        return LCA

    def helper(self, node, A, B):

        """返回是否发现了A、B以及当前找到的LCA"""

        if not node:

            return False, False, None

        lA, lB, l_LCA = self.helper(node.left, A, B)

        rA, rB, r_LCA = self.helper(node.right, A, B)

        # 如果A、B是在不同子树下找到的,

        # 那么当前节点就是LCA

        if (lA and rB) or (rA and lB):

            return True, True, node

        find_A = lA or rA or node == A

        find_B = lB or rB or node == B

        LCA = l_LCA or r_LCA

        # 如果A、B在子树中(不管是否同一子树)找到了,

        # 且当前节点就是A或B,那么当前节点绝对是LCA

        if find_A and find_B and (node == A or node == B):

            LCA = node

        return find_A, find_B, LCA

2、DFS then Common Route

"""

Definition of TreeNode:

class TreeNode:

    def __init__(self, val):

        this.val = val

        this.left, this.right = None, None

"""

class Solution:

    """

    @param: root: The root of the binary tree.

    @param: A: A TreeNode

    @param: B: A TreeNode

    @return: Return the LCA of the two nodes.

    """

    def lowestCommonAncestor3(self, root, A, B):

        # A、B的公共路径

        self.route_AB = []

        # A的路径

        self.route_A = []

        # B的路径

        self.route_B = []

        self.dfs(root, A, B)

        # 说明A/B不在树中

        if not (self.route_A and self.route_B):

            return None

        # 注意要反转顺序,否则直接返回根节点

        self.route_AB.reverse()

        for node in self.route_AB:

            if node in self.route_A and node in self.route_B:

                return node

        return None

    def dfs(self, node, A, B):

        if not node:

            return

        self.route_AB.append(node)

        if node == A:

            self.route_A = [n for n in self.route_AB]

        if node == B:

            self.route_B = [n for n in self.route_AB]

        # 如果A、B的路径都找到了,就无需往深处遍历了

        if self.route_A and self.route_B:

            return

        self.dfs(node.left, A, B)

        self.dfs(node.right, A, B)

        # 因为route_AB代表的是A和B的公共路径

        # 若当前节点遍历完但A、B仍有其一路径

        # 未构造,则说明当前节点不是A、B的公共部分

        # 所以将其在route_AB弹出

        if not (self.route_A and self.route_B):

            self.route_AB.pop()

3、设置父节点,比较公共祖先

"""

Definition of TreeNode:

class TreeNode:

    def __init__(self, val):

        this.val = val

        this.left, this.right = None, None

"""

class Solution:

    """

    @param: root: The root of the binary tree.

    @param: A: A TreeNode

    @param: B: A TreeNode

    @return: Return the LCA of the two nodes.

    """

    def lowestCommonAncestor3(self, root, A, B):

        self.set_parent(root, None)

        # 若A/B没有父节点,说明它们不在这棵树中,返回None

        if not (hasattr(A, 'parent') and hasattr(B, 'parent')):

            return None

        # 将A的所有祖先(包括自己)加入列表

        parents = [A]

        node = A.parent

        while node:

            parents.append(node)

            node = node.parent

        # 从B开始递归它的所有祖先(包括自己)

        # 看是否在A的祖先们中

        node = B

        while node:

            if node in parents:

                return node

            node = node.parent

        return None

    def set_parent(self, node, parent):

        """为每个节点设置父节点"""

        if not node:

            return

        node.parent = parent

        self.set_parent(node.left, node)

上一篇下一篇

猜你喜欢

热点阅读