树与图中等3 种解法
#235二叉搜索树的最近公共祖先
在二叉搜索树中寻找两个给定节点的最近公共祖先。
#二叉搜索树#深度优先搜索#路径
原题
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 输出: 6 解释: 节点2和节点8的最近公共祖先是6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 输出: 2 解释: 节点2和节点4的最近公共祖先是2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉搜索树中。
解题主线
- 若 p、q 同在当前值一侧就继续向该侧走,否则当前节点就是分叉点。
解法 1:递归利用 BST
根据两个目标相对根的位置递归进入唯一可能的子树。
-
时间复杂度: O(h)
-
空间复杂度: O(h)
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// BST 的有序性保证每次只需沿唯一可能包含两个目标的方向搜索。
// 最近公共祖先就是两条根路径最后共有的节点,也就是搜索方向首次分叉处。
if (root == null) return null;
if (p.val < root.val && q.val < root.val) {
return lowestCommonAncestor(root.left, p, q);
}
if (p.val > root.val && q.val > root.val) {
return lowestCommonAncestor(root.right, p, q);
}
return root;
}
}解法 2:迭代寻找分叉点
沿 BST 单路径移动,直到两个目标位于两侧或命中当前节点。
-
时间复杂度: O(h)
-
空间复杂度: O(1)
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// BST 的有序性保证每次只需沿唯一可能包含两个目标的方向搜索。
// 最近公共祖先就是两条根路径最后共有的节点,也就是搜索方向首次分叉处。
TreeNode current = root;
while (current != null) {
if (p.val < current.val && q.val < current.val) current = current.left;
else if (p.val > current.val && q.val > current.val) current = current.right;
else return current;
}
return null;
}
}解法 3:比较根到节点路径
分别生成根到 p、q 的路径,最后一个相同节点即最近公共祖先。
-
时间复杂度: O(h)
-
空间复杂度: O(h)
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// BST 的有序性保证每次只需沿唯一可能包含两个目标的方向搜索。
// 最近公共祖先就是两条根路径最后共有的节点,也就是搜索方向首次分叉处。
List<TreeNode> first = pathTo(root, p);
List<TreeNode> second = pathTo(root, q);
TreeNode answer = null;
for (int i = 0; i < Math.min(first.size(), second.size()); i++) {
if (first.get(i) != second.get(i)) break;
answer = first.get(i);
}
return answer;
}
private List<TreeNode> pathTo(TreeNode root, TreeNode target) {
List<TreeNode> path = new ArrayList<>();
TreeNode current = root;
while (current != null) {
path.add(current);
if (current == target) break;
current = target.val < current.val ? current.left : current.right;
}
return path;
}
}边界与易错点
- 比较的是 p、q 的值与当前节点值,不是只比较 p 和 q。
- 组合文件与独立文件的重复递归、迭代实现需合并。
整理来源
由旧仓库源码复核、去重并整理;展示代码已按 Java 21 语义修正明显问题。
leetcode/src/main/java/tree/Q235_236_lowestCommonAncestor.javaleetcode/src/main/java/tree/Q235_bst_lowestCommonAncestor.java