Since each node has a parent pointer, we can trace the path from p to the root and store all visited nodes in a set. Then we trace from q upward until we find a node that already exists in the set. This intersection point is the lowest common ancestor.
p to the root, adding each node to the set.q to the root, checking if each node exists in the set."""
# Definition for a Node.
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.parent = None
"""
class Solution:
def lowestCommonAncestor(self, p: 'Node', q: 'Node') -> 'Node':
seen = set()
while p:
seen.add(p)
p = p.parent
while q:
if q in seen:
return q
q = q.parentIf we know the depth of both nodes, we can align them first. We move the deeper node upward until both nodes are at the same depth. Then we move both nodes upward in lockstep until they meet. The meeting point is the LCA. This avoids using extra space for a hash set.
p and q."""
# Definition for a Node.
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.parent = None
"""
class Solution:
def lowestCommonAncestor(self, p: 'Node', q: 'Node') -> 'Node':
def height(node):
h = 0
while node:
h += 1
node = node.parent
return h
h1, h2 = height(p), height(q)
if h2 < h1:
p, q = q, p
diff = abs(h1 - h2)
while diff:
q = q.parent
diff -= 1
while p != q:
p = p.parent
q = q.parent
return pThis approach is similar to finding the intersection of two linked lists. We use two pointers starting at p and q. When a pointer reaches the root (null), we redirect it to the other starting node. After at most two passes, both pointers will have traveled the same total distance and will meet at the LCA.
ptr1 = p and ptr2 = q.ptr1 != ptr2:ptr1 to its parent, or to q if it reaches null.ptr2 to its parent, or to p if it reaches null.ptr1 (or ptr2) as the LCA."""
# Definition for a Node.
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.parent = None
"""
class Solution:
def lowestCommonAncestor(self, p: 'Node', q: 'Node') -> 'Node':
ptr1, ptr2 = p, q
while ptr1 != ptr2:
ptr1 = ptr1.parent if ptr1 else q
ptr2 = ptr2.parent if ptr2 else p
return ptr1When using the height-based iteration approach, a common mistake is assuming p and q are always at different depths. If both nodes are at the same level, no adjustment is needed before the lockstep traversal. Failing to handle this case correctly can lead to infinite loops or null pointer exceptions.
Since nodes can have duplicate values, comparing p.val == q.val is incorrect. You must compare node references (p == q or p is q) to determine when the two pointers have met at the same node. Using value comparison will produce wrong results when different nodes have the same value.
This problem is essentially finding the intersection of two linked lists (the paths from p and q to the root). The elegant two-pointer solution works because both pointers travel the same total distance before meeting. Missing this insight leads to more complex solutions using extra space for hash sets when O(1) space is achievable.