236.二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点5
和节点1
的最近公共祖先是节点3 。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点5
和节点4
的最近公共祖先是节点5 。
因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入: root = [1,2], p = 1, q = 2
输出: 1
提示:
- 树中节点数目在范围 [ 2 , 1 0 5 ] [2, 10^5] [2,105] 内。
- − 1 0 9 ≤ N o d e . v a l ≤ 1 0 9 -10^9 \leq Node.val \leq 10^9 −109≤Node.val≤109
- 所有
Node.val
互不相同
。 p != q
p
和q
均存在于给定的二叉树中。
解法一(DFS)
思路分析:
- 首先根据题目要求,最近公共祖先为,深度尽可能大的且包括所给节点的节点
- 且二叉树的深度为,从根节点到该节点的最简单路径长度
- 因此使用递归后序遍历,从深度最大的节点开始判断该节点是否为最近公共祖先
- 即每遍历一个节点,则从该节点进行查找,判断是否能查找到题目所给节点
实现代码如下:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return dfs(root, p.val, q.val);
}
private TreeNode dfs(TreeNode node, int p, int q) {
if (node == null)
return null; // 空节点停止判断
TreeNode leftDfs = dfs(node.left, p, q); // 先从左子树判断
if (leftDfs != null) // 若左子树已找到公共祖先 则直接返回
return leftDfs;
TreeNode rightDfs = dfs(node.right, p, q); // 再判断右子树
if (rightDfs != null) // 若右子树已找到公共祖先 则直接返回
return rightDfs;
boolean findP = findTreeNode(node, p); // 判断节点p是否在该子树上
boolean findQ = findTreeNode(node, q); // 判断节点q是否在该子树上
if (findQ && findP)
return node; // 若两节点均在该树上 则返回公共祖先
return null;
}
private Boolean findTreeNode(TreeNode node, int x) {
if (node == null)
return false;
return findTreeNode(node.right, x) || findTreeNode(node.left, x) || node.val == x;
}
}
提交结果如下:
解答成功:
执行耗时:7 ms,击败了65.99% 的Java用户
内存消耗:43.6 MB,击败了45.60% 的Java用户
复杂度分析:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2),每遍历一个节点,需要重新遍历二叉树寻找目标节点
- 空间复杂度: O ( n 2 ) O(n^2) O(n2),递归遍历二叉树的同时,还需递归寻找目标节点
优化解法一
思路分析:
- 对于解法一每遍历一个节点,判断其是否为公共祖先,尝试优化
- 在遍历二叉树的过程中,寻找到最近公共祖先
- 然后考虑对二叉树进行后序遍历,当遍历节点为
null
时返回null
- 当遍历到
p
或q
时,找到目标节点将其返回 - 然后处理再遍历左右子树,并保留左右子树返回节点
- 当左右子树都不为
null
时,说明p
和q
节点分别位于二叉树两侧 - 考虑完两个节点位于异侧情况时,只需考虑两节点位于二叉树的一侧
- 即当左子树遍历得到节点不为
null
时,左子树遍历得到的节点即为最近公共祖先 - 对于右子树遍历得到节点同理。
- 即当左子树遍历得到节点不为
实现代码如下:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return dfs(root, p, q);
}
private TreeNode dfs(TreeNode node, TreeNode p, TreeNode q) {
if (node == null || node == p || node == q)
return node;
TreeNode left = dfs(node.left, p, q);
TreeNode right = dfs(node.right, p, q);
if (left != null && right != null)
return node;
if (left != null)
return left;
return right;
}
}
提交结果如下:
解答成功:
执行耗时:7 ms,击败了65.99% 的Java用户
内存消耗:43.8 MB,击败了22.74% 的Java用户
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),对于二叉树只需遍历一遍
- 空间复杂度: O ( n ) O(n) O(n)