原题
树与图中等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 为不同节点且均存在于给定的二叉搜索树中。

查看原题

解题主线

  1. 若 p、q 同在当前值一侧就继续向该侧走,否则当前节点就是分叉点。

解法 1:递归利用 BST

根据两个目标相对根的位置递归进入唯一可能的子树。

  • 时间复杂度: O(h)

  • 空间复杂度: O(h)

JAVA
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)

JAVA
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)

JAVA
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.java
  • leetcode/src/main/java/tree/Q235_bst_lowestCommonAncestor.java