题目描述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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 。因为根据定义最近公共祖先节点可以为节点本身。
- 输入: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)。