Idea 1: First assume that the tree is a binary search tree
First of all, let's explain what a binary search tree is:
in a binary search tree, for each node, the value in its left subtree is smaller than him, and the value in the right subtree is larger than him . So an in-order traversal of a binary search tree is an ordered set of data.
For the tree above, assume that the most recent common ancestor of pq is required.
Then it has the following situations:
For ordinary binary trees, there are nothing more than these situations: pq are all on the left, pq are all on the right, pq is one left and one right, and one of pq is the root node.
Therefore, we recursively go to the left subtree and the right subtree to find the common ancestor of the pq node. If it is found, it will return the node, and if it is not found, it will return empty.
According to the above ideas, we can easily write the code
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
// p 为当前树的根节点
if(p == root) return p;
// q 为当前树的根节点
if(q == root) return q;
// 去左子树中找
TreeNode left = lowestCommonAncestor(root.left,p,q);
// 去右子树中找
TreeNode right = lowestCommonAncestor(root.right,p,q);
// 左边右边都找到了
if(left != null && right != null) {
return root;
}
// 左边找到了,右边没找到
if(left != null) {
return left;
}
if(right != null) {
return right;
}
return null;
}
Idea 2: Assume that the tree is represented by children's parents
Each node will save the address of its parent node, which can be searched online layer by layer until it finds the first intersection of the two linked lists, which is their common ancestor.
For a common binary tree, it can only be searched down layer by layer, not up, so the path of two nodes should be reserved until the last identical node of the two paths. Here we use a stack to keep the path of two nodes.
The elements in the stack with more elements are popped first, and then the two stacks are popped together until the nodes to be popped are equal, which is their nearest common ancestor.
So the biggest difficulty here is the storage path.
Here, the stack is used to store the path. When a node is traversed, the node is put into the stack, and then the left and right trees of the node are searched recursively. If it is found, the path is retained, and if it is not found, it is popped up.
Suppose to find p in the figure below:
first put the root node on the stack, recursively search the left subtree of the root node, if not found, pop it up and search in the right subtree.
When root goes to 6, it is found that the left and right sides of the node are empty, indicating that the target node is not found in the subtree, 6 is popped up, and the search continues in the right subtree of 5.
Similarly, it cannot be found in the right subtree of 5, and it will pop up until it goes to the right subtree of 3 to find it, and it comes to 1 and finds it.
// 用于找节点的路径
public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
if(root == null || node == null) {
return false;
}
// 将当前节点放入栈中
stack.push(root);
if(root.val == node.val) {
return true;// 找到了
}
// 当前节点没找到,去左子树找
boolean flag = getPath(root.left,node,stack);
// 左子树中找到了,直接返回
if(flag) {
return true;
}
// 左子树没找到,去右子树找
flag = getPath(root.right,node,stack);
// 右子树中找到了,直接返回
if(flag) {
return true;
}
// 左右子树都没找到,弹出节点
stack.pop();
return false;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
Stack<TreeNode> stackp = new Stack<>();
Stack<TreeNode> stackq = new Stack<>();
// 分别得到 p q 的路径
getPath(root,p,stackp);
getPath(root,q,stackq);
int sizep = stackp.size();
int sizeq = stackq.size();
if(sizep > sizeq) {
int size = sizep - sizeq;
// 弹出元素直至两栈中元素个数相等
while(size > 0) {
stackp.pop();
size--;
}
}else {
int size = sizeq - sizep;
// 弹出元素直至两栈中元素个数相等
while(size > 0) {
stackq.pop();
size--;
}
}
// 一起弹出,直到找到第一个相同的元素
while(!stackp.isEmpty() && !stackq.isEmpty()) {
if(stackp.peek() == stackq.peek()) {
// 找到了,就返回该节点
return stackq.pop();
}else {
stackp.pop();
stackq.pop();
}
}
// 没找到,返回 null
return null;
}