Given a binary tree, find the nearest common ancestor of two specified nodes in the tree.
Example 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
Example 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
Example 3:
输入:root = [1,2], p = 1, q = 2
输出:1
Problem solving ideas
First of all, the function signature of this problem is as follows:
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q);
root
node identifies a binary tree, p
and q
are two nodes on this binary tree, allowing you to return the nearest common ancestor of the p
node and the q
node.
The routines of all binary trees are the same. You can write the traversal framework first:
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 前序遍历
TreeNode left = lowestCommonAncestor(root.left, p, q);
// 中序遍历
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 后序遍历
}
Now we think about how to add some details and transform the framework into a solution. When encountering any recursive problem, it is nothing more than three questions of the soul:
1. What does this function do?
2. What are the variables in the parameters of this function?
3. What should you do to get the recursive result of the function?
Similar to the idea of dynamic programming, it is also necessary to clearly define "definition", "state" and "choice".
1) What does this function do?
Describe the "definition" of the function lowestCommonAncestor
.
Description: Input three parameters root
, p
, q
to this function, it will return a node.
Case 1, if p
and q
are in the tree rooted at root
, the function returns the nearest common ancestor node of p
and q
.
Case 2, if p
nor q
are in the tree rooted at root
, then null
will be returned as a matter of course.
Case 3, if only one of p
and q
exists in the tree rooted at root
, the function returns that node.
The title says that the inputp
andq
must exist in the tree rooted atroot
, but in the recursive process, the above three situations may occur, so it is necessary to define clearly here, and the subsequent definitions will be reflected in the code
2) Among the parameters of this function, what are the variables?
Describe the "state" of this function.
Description: The variable in the function parameter is root
, because according to the framework, lowestCommonAncestor(root)
will recursively call root.left
and root.right
; as for p
and q
, we ask for their common ancestor, they will definitely not change, that is, one layer in the recursive process Pass it through layer by layer.
You can also understand that this is "state transition", what is each recursion doing? Isn't it just transferring "root as the root" to "rooting the child nodes of root", and constantly reducing the scale of the problem?
3) Get the recursive result of the function, what should you do?
After getting the result of the recursive call, what "choice" do you make?
Think about the base case first, if root
is empty, it must return null
. If root
itself p
or q
, for example root
is p
node it, if q
exist in order to root
rooted tree, apparently root
is the most recent common ancestor; even q
does not exist in order to root
root of the tree, according to the situation The definition of 3 should also return root
node.
The base case of the above two cases can fill in the framework code a little:
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 如果 root 为空
if (root == null) return null;
// 如果 root 本身就是 p 或者 q
if (root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
}
Now do something with the results of the recursive calls left
and right
. According to the definition of the function in the first question just now, we continue to discuss it by situation:
Case 1, if p
and q
are in the tree rooted at root
, then left
and right
must be p
and q
respectively (as seen from the base case).
Case 2, if p
nor q
are in the tree rooted at root
, return null
directly.
Case 3, if only one of p
and q
exists in the tree rooted at root
, the function returns that node.
After understanding the above three points, you can directly look at the solution code:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// base case
if (root == null) return null;
if (root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 情况 1
if (left != null && right != null) {
return root;
}
// 情况 2
if (left == null && right == null) {
return null;
}
// 情况 3
return left == null ? right : left;
}
}
Personal understanding:
Search the node at the pre-order position, if it is an empty node, return it directly, if the search
p
orq
, return the node, otherwise continue to recurseReceive the return value of the pre-order at the post-order position. If
left
andright
are not empty, it means that they arep
andq
respectively. The currentroot
is the nearest common ancestor, and theroot
node is returned directly. If one is empty and the other is not empty, it means that a node is found, and the node is passed up to find another node until both are not empty. At this time,root
is the nearest common ancestor, and theroot
node is returned directly.
For case 1, you must have doubts, left
and right
not empty, they are p
and q
respectively. It can be said that root
is their common ancestor, but can you be sure that root
is the "most recent" common ancestor?
This is a clever place, because here is the post-order traversal of the binary tree! pre-order traversal can be understood as from top to bottom, while post-order traversal is from bottom to top, just like starting from p
and q
and going up, the first intersecting node is this root
, you said this is the most recent public What about ancestors?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。