算法:
- 从根节点开始遍历树。
- 如果当前节点本身是 p 或 q 中的一个,我们会将变量 mid 标记为 true,并继续搜索左右分支中的另一个节点。
- 如果左分支或右分支中的任何一个返回 true,则表示在下面找到了两个节点中的一个。
- 如果在遍历的任何点上,左、右或中三个标志中的任意两个变为 true,这意味着我们找到了节点 p 和 q 的最近公共祖先。
程序:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
# Variable to store LCA node.
self.ans = None
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
def recurse_tree(current_node):
# If reached the end of a branch, return False.
if not current_node:
return False
# Left Recursion
left = recurse_tree(current_node.left)
# Right Recursion
right = recurse_tree(current_node.right)
# If the current node is one of p or q
mid = current_node == p or current_node == q
# If any two of the three flags left, right or mid become True.
if mid + left + right >= 2:
self.ans = current_node
# Return True if either of the three bool values is True.
return mid or left or right
# Traverse the tree
recurse_tree(root)
return self.ans
解法二:父指针
算法:
- 从根节点开始遍历树。
- 在找到 p 和 q 之前,将父指针存储在字典中。
- 一旦我们找到了 p 和 q,我们就可以使用父亲字典获得 p 的所有祖先,并添加到一个称为祖先的集合中。
- 同样,我们遍历节点 q 的祖先。如果祖先存在于为 p 设置的祖先中,这意味着这是 p 和 q 之间的第一个共同祖先(同时向上遍历),因此这是 LCA 节点。
程序:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
# Variable to store LCA node.
self.ans = None
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
# Stack for tree traversal
stack = [root]
# Dictionary for parent pointers
parent = {root: None}
# Iterate until we find both the nodes p and q
while p not in parent or q not in parent:
node = stack.pop()
# While traversing the tree, keep saving the parent pointers.
if node.left:
parent[node.left] = node
stack.append(node.left)
if node.right:
parent[node.right] = node
stack.append(node.right)
# Ancestors set() for node p.
ancestors = set()
# Process all ancestors for node p using parent pointers.
while p:
ancestors.add(p)
p = parent[p]
# The first ancestor of q which appears in
# p's ancestor set() is their lowest common ancestor.
while q not in ancestors:
q = parent[q]
return q
解法三:
算法:
- 从根节点开始。
- 将 (root, root_state) 放在堆栈上。root_state 定义要遍历该节点的一个子节点还是两个子节点。
- 当堆栈不为空时,查看堆栈的顶部元素,该元素表示为 (parent_node, parent_state)。
- 在遍历 parent_node 的任何子节点之前,我们检查 parent_node 本身是否是 p 或 q 中的一个。
- 当我们第一次找到 p 或 q 的时候,设置一个布尔标记,名为 one_node_found 为 true 。还可以通过在变量 LCA_index 中记录堆栈的顶部索引来跟踪最近的公共祖先。因为堆栈的所有当前元素都是我们刚刚发现的节点的祖先。
- 第二次 parent_node == p or parent_node == q 意味着我们找到了两个节点,我们可以返回 LCA node。
- 每当我们访问 parent_node 的子节点时,我们将 (parent_node, updated_parent_state) 推到堆栈上。我们更新父级的状态为子级/分支已被访问/处理,并且相应地更改状态。
- 当状态变为 BOTH_DONE 时,最终会从堆栈中弹出一个节点,这意味着左、右子树都被推到堆栈上并进行处理。如果 one_node_found 是 true 的,那么我们需要检查被弹出的顶部节点是否可能是找到的节点的祖先之一。在这种情况下,我们需要将LCA_index减少一个。因为其中一位祖先被弹出了。
- 当同时找到 p 和 q 时,LCA_index 将指向堆栈中包含 p 和 q 之间所有公共祖先的索引。并且 LCA_index 元素具有p和q之间的最近公共祖先。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# Three static flags to keep track of post-order traversal.
# Both left and right traversal pending for a node.
# Indicates the nodes children are yet to be traversed.
BOTH_PENDING = 2
# Left traversal done.
LEFT_DONE = 1
# Both left and right traversal done for a node.
# Indicates the node can be popped off the stack.
BOTH_DONE = 0
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
# Initialize the stack with the root node.
stack = [(root, Solution.BOTH_PENDING)]
# This flag is set when either one of p or q is found.
one_node_found = False
# This is used to keep track of LCA index.
LCA_index = -1
# We do a post order traversal of the binary tree using stack
while stack:
parent_node, parent_state = stack[-1]
# If the parent_state is not equal to BOTH_DONE,
# this means the parent_node can't be popped of yet.
if parent_state != Solution.BOTH_DONE:
# If both child traversals are pending
if parent_state == Solution.BOTH_PENDING:
# Check if the current parent_node is either p or q.
if parent_node == p or parent_node == q:
# If one_node_found is set already, this means we have found both the nodes.
if one_node_found:
return stack[LCA_index][0]
else:
# Otherwise, set one_node_found to True,
# to mark one of p and q is found.
one_node_found = True
# Save the current top index of stack as the LCA_index.
LCA_index = len(stack) - 1
# If both pending, traverse the left child first
child_node = parent_node.left
else:
# traverse right child
child_node = parent_node.right
# Update the node state at the top of the stack
# Since we have visited one more child.
stack.pop()
stack.append((parent_node, parent_state - 1))
# Add the child node to the stack for traversal.
if child_node:
stack.append((child_node, Solution.BOTH_PENDING))
else:
# If the parent_state of the node is both done,
# the top node could be popped off the stack.
# i.e. If LCA_index is equal to length of stack. Then we decrease LCA_index by 1.
if one_node_found and LCA_index == len(stack) - 1:
LCA_index -= 1
stack.pop()
return None
解法四:暴力法
超出时间限制
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.num = []
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
self.make(root,p,q)
return self.num[-1]
def make(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root == None:
return
if self.judge_nodeinroot(root,p) and self.judge_nodeinroot(root,q):
self.num.append(root)
if self.judge_nodeinroot(root.left,p) and self.judge_nodeinroot(root.left,q):
self.make(root.left,p,q)
else:
self.make(root.right,p,q)
# 判断root是否是node的祖先
def judge_nodeinroot(self,root, node):
if root == None:
return False
if root == node:
return True
return self.judge_nodeinroot(root.left,node) or self.judge_nodeinroot(root.right,node)