题目
设计并实现一个算法,找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。注意:这不一定是二叉搜索树。
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
3
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
示例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: 后序遍历递归查找
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
find_p = None
def LRD(root, p, q, find_p):
if root == None:
return False, find_p
lfind, find_p = LRD(root.left, p, q, find_p)
rfind, find_p = LRD(root.right, p, q, find_p)
# print(root)
# print(lfind)
# print(rfind)
if lfind and rfind or ((root == p or root == q) and (lfind or rfind)):
find_p = root
return root == p or root == q or lfind or rfind, find_p
_, find_p = LRD(root, p, q, find_p)
return find_p
方法2: 采用额外的字典记录每一个节点及其父节点的对应关系,然后我们就可以利用节点的父节点信息从 p 结点开始不断往上跳,并记录已经访问过的节点,再从 q 节点开始不断往上跳,如果碰到已经访问过的节点,那么这个节点就是我们要找的首个共同祖先。
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
map_dict = {}
map_dict[root.val] = None
def dfs(root):
if root == None:
return None
left = root.left
right = root.right
left_val = dfs(left)
right_val = dfs(right)
if left_val != None:
map_dict[left_val] = root
if right_val != None:
map_dict[right_val] = root
return root.val
dfs(root)
p_list = []
p_list.append(p)
while p != None:
p_list.append(map_dict[p.val])
p = map_dict[p.val]
# print(p_list)
while q != None:
if q in p_list:
return q
q = map_dict[q.val]
return None