leetcode 236:二叉树的最近公共祖先

本文介绍两种方法解决二叉树中给定两个节点p和q的最近公共祖先问题:一种是利用父节点存储和回溯,另一种是通过递归遍历并定义f_x辅助判断。这两种方法都达到O(n)的时间复杂度,适用于各种树形结构查找问题。
摘要由CSDN通过智能技术生成

题目描述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例:
1.在这里插入图片描述
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
2:
在这里插入图片描述
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

  1. 输入:root = [1,2], p = 1, q = 2
    输出:1
    说明:所有 Node.val 互不相同 。p != q,p 和 q 均存在于给定的二叉树中。

思路1:存储父结点
遍历一遍树,为每一个结点存储父结点,然后p、q从自身出发,向上回溯到根节点,得到根到自身的路径,将两条路径进行比较,最先重合的点就是两者的最近公共祖先。

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

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        result = {}
        father = None
        path_p = [p]
        path_q = [q]
        self.preorder_father(root, result, father)
        father = result[p]
        while father:
            path_p.append(father)
            father = result[father]
        father = result[q]
        while father:
            path_q.append(father)
            father = result[father]
        for i in range(len(path_p)):
            if path_p[i] in path_q:
                return path_p[i]

    def preorder_father(self, root, result, father):
        if not root:
            return
        result[root] = father
        self.preorder_father(root.left, result, root)
        self.preorder_father(root.right, result, root)

时间复杂度和空间复杂度都为O(n),空间的解释:递归,要存储的信息则是和树的高度有关的,要是树是单链的形状,则为O(n)。
思路2:
我们递归遍历整棵二叉树,定义 f_x表示 x 节点的子树中是否包含 p 节点或 q 节点,如果包含为 true,否则为 false。那么符合条件的最近公共祖先 xx 一定满足如下条件:
(f_lson&& f_rson) || ((x= p||x =q)&& (f_lson || f_rson))
其中 lson 和rson 分别代表 x 节点的左孩子和右孩子。初看可能会感觉条件判断有点复杂,我们来一条条看,f_lson&& f_rson说明左子树和右子树均包含 p 节点或 q 节点,如果左子树包含的是 p 节点,那么右子树只能包含q 节点,反之亦然,因为 p 节点和 q 节点都是不同且唯一的节点,因此如果满足这个判断条件即可说明 x 就是我们要找的最近公共祖先。再来看第二条判断条件,这个判断条件即是考虑了 x 恰好是 p 节点或 q 节点且它的左子树或右子树有一个包含了另一个节点的情况,因此如果满足这个判断条件亦可说明 x 就是我们要找的最近公共祖先。

你可能会疑惑这样找出来的公共祖先深度是否是最大的。其实是最大的,因为我们是自底向上从叶子节点开始更新的(递归的思想),所以在所有满足条件的公共祖先中一定是深度最大的祖先先被访问到,且由于 f_x本身的定义很巧妙,在找到最近公共祖先 x 以后,f_x按定义被设置为 true ,即假定了这个子树中只有一个 p 节点或 q 节点,因此其他公共祖先不会再被判断为符合条件。

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

class Solution(object):
    def __init__(self):
        self.res = None
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        self.dfs(root, p, q)
        return self.res

    def dfs(self, root, p, q):
        if not root:
            return False
        left = self.dfs(root.left, p, q)
        right = self.dfs(root.right, p, q)
        if (left and right) or ((root.val == p.val or root.val == q.val) and (left or right)):
            self.res = root
        bool = left or right or (root.val == p.val or root.val == q.val)
        return bool

时间复杂度为O(n),最坏空间复杂度为O(n)。空间解释:二叉树的递归调用取决于树的高度,当二叉树是一条链的时候,高度为n,所以最坏情况复杂度为O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值