# 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':
a, b = p, q
# Traverse up both nodes, switching to the other node when reaching root
while a != b:
a = a.parent if a else q
b = b.parent if b else p
return a
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* parent;
Node(int _val) {
val = _val;
left = nullptr;
right = nullptr;
parent = nullptr;
}
};
class Solution {
public:
Node* lowestCommonAncestor(Node* p, Node* q) {
Node* a = p;
Node* b = q;
while (a != b) {
a = a ? a->parent : q;
b = b ? b->parent : p;
}
return a;
}
};
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node parent;
public Node(int val) {
this.val = val;
this.left = null;
this.right = null;
this.parent = null;
}
}
*/
class Solution {
public Node lowestCommonAncestor(Node p, Node q) {
Node a = p, b = q;
while (a != b) {
a = (a != null) ? a.parent : q;
b = (b != null) ? b.parent : p;
}
return a;
}
}
/**
* // Definition for a Node.
* function Node(val) {
* this.val = val;
* this.left = this.right = this.parent = null;
* }
*/
/**
* @param {Node} p
* @param {Node} q
* @return {Node}
*/
var lowestCommonAncestor = function(p, q) {
let a = p, b = q;
while (a !== b) {
a = a ? a.parent : q;
b = b ? b.parent : p;
}
return a;
};
You are given two nodes, p
and q
, of a binary tree. Each node contains a pointer to its parent node (i.e., node.parent
), in addition to its left and right children. Your task is to find the lowest common ancestor (LCA) of these two nodes. The LCA of two nodes is defined as the lowest node in the tree that has both p
and q
as descendants (where a node can be a descendant of itself).
p
and q
are guaranteed to exist in the tree.
The main challenge is to find the lowest common ancestor of two nodes in a binary tree where each node has a parent pointer. Unlike the classic LCA problem (where you start from the root), here you can move up from any node to the root using the parent
pointer.
A brute-force approach would be to traverse up from one node (p
), record all ancestors in a set, then traverse up from the other node (q
) and return the first ancestor found in the set. This works, but can be improved.
Thinking further, if you consider the paths from p
and q
to the root as two linked lists, the problem becomes similar to finding the intersection node of two singly linked lists. By traversing up both at the same speed and switching to the start of the other list when reaching the end, you guarantee that both pointers will meet at the LCA.
The optimized solution leverages the parent pointers and the analogy to linked list intersection:
a
and b
, at nodes p
and q
respectively.null
(root's parent), reset it to the other starting node.
This works because both pointers traverse the same number of steps (the sum of the distances from p
and q
to the root), ensuring they sync up at the LCA.
Consider the following tree:
3 / \ 5 1 / \ \ 6 2 8
Suppose p = 6
and q = 8
.
a
starts at 6, pointer b
starts at 8.a
goes to 5, b
goes to 1.a
to 3, b
to 3.If the depths are different, the switching ensures both pointers traverse the same total path length and meet at the LCA.
The optimized approach is both time and space efficient.
The key insight is to treat the ancestor chain as a singly linked list and use two pointers to find their intersection (lowest common ancestor). By leveraging parent pointers and pointer switching, we achieve an elegant, constant-space solution that is efficient and easy to implement.