📅  最后修改于: 2023-12-03 14:49:01.172000             🧑  作者: Mango
在二叉树中,给定任意数量的节点,它们的最小公共祖先(Lowest Common Ancestor,LCA)是指最深的(离根节点最远的)同时拥有这些节点的祖先节点。
如下图所示,节点 3 和节点 4 的 LCA 是节点 1,而节点 2 和节点 4 的 LCA 是节点 2。
解法一:递归
最直观的解法是递归。对于当前节点 root,如果其中至少有一个目标节点,则返回 root。否则,递归到它的左子树和右子树,分别记作 left 和 right。如果 left 和 right 都非空,则说明目标节点分别在左右子树上,因此 root 为 LCA;如果 left 或 right 有且仅有一个非空,说明目标节点都在该非空节点的子树中,该非空节点为 LCA;否则,两个子树中均不包含目标节点,返回空。
示例代码如下:
class Solution(object):
def lowestCommonAncestor(self, root, nodes):
if not root or root in nodes:
return root
left, right = [self.lowestCommonAncestor(child, nodes) for child in (root.left, root.right)]
return root if left and right else left or right
时间复杂度:每个节点仅被遍历一次,因此时间复杂度为 O(n),其中 n 是节点数。
空间复杂度:每次递归调用栈的深度不会超过树的高度 H,即 O(H)。最坏情况下树呈链状,H=n,因此空间复杂度为 O(n)。
解法二:存储父节点
上述递归解法从上向下递归查找 LCA。解法二换个思路,从下向上查找 LCA。解法二的前提是,题目中给定的节点之间存在 LCA。如果题目给定的节点之间不存在 LCA,则解法二无法处理。
我们可以先遍历一遍二叉树,用哈希表记录每个节点的父节点,并将所有目标节点存放到集合中。随后,从任意一个目标节点开始,沿着父节点指针不断向上跳,直到到达根节点,并在哈希表中保存已遍历过的所有节点。随后,从另一个目标节点开始,同样不断向上跳,如果碰到在哈希表中已经出现过的节点,则该节点为 LCA。否则,继续向上跳。
示例代码如下:
class Solution(object):
def lowestCommonAncestor(self, root, nodes):
parent = {root: None}
def dfs(node):
if node.left:
parent[node.left] = node
dfs(node.left)
if node.right:
parent[node.right] = node
dfs(node.right)
dfs(root)
ancestors = set()
while nodes:
node = nodes.pop()
while node:
if node in ancestors:
return node
ancestors.add(node)
node = parent[node]
return None
时间复杂度:第一次遍历用时 O(n),随后的查找用时取决于树的高度 H,即 O(H)。最坏情况下树呈链状,H=n,因此时间复杂度为 O(n)。
空间复杂度:哈希表和集合分别存储了所有节点和目标节点,因此空间复杂度为 O(n)。
本文介绍了二叉树中任意数量节点的最小公共祖先问题,给出了两种解法:递归解法和存储父节点解法。递归解法需要从上向下递归查找 LCA,时间复杂度为 O(n),空间复杂度为 O(n)。存储父节点解法则需要从下向上查找 LCA,时间复杂度为 O(n),空间复杂度为 O(n)。