GitHub_Trending/le/LeetCode-Book:二叉树最近公共祖先问题详解

GitHub_Trending/le/LeetCode-Book:二叉树最近公共祖先问题详解

【免费下载链接】LeetCode-Book 《剑指 Offer》 Python, Java, C++ 解题代码,LeetBook《图解算法数据结构》配套代码仓 【免费下载链接】LeetCode-Book 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Book

引言:你还在为LCA问题抓狂吗?

在二叉树(Binary Tree)相关算法题中,最近公共祖先(Lowest Common Ancestor, LCA) 问题被誉为"面试高频考点"。无论是求职面试还是算法竞赛,这个问题都频繁出现,且衍生出多种变形。本文将基于LeetCode-Book项目中的经典题解,从定义解构、算法推导到代码实现,全方位剖析两种最典型的LCA场景——二叉搜索树(BST)普通二叉树的LCA求解方案。读完本文,你将掌握:

  • 3种LCA存在形式的数学定义
  • BST的2种最优解法(迭代/递归)及复杂度对比
  • 普通二叉树的回溯算法实现原理
  • 3种编程语言(Python/Java/C++)的代码模板
  • 5道扩展习题及解题思路

一、概念解构:什么是最近公共祖先?

1.1 祖先与最近公共祖先的数学定义

祖先(Ancestor):若节点p在节点root的左/右子树中,或p = root,则称rootp的祖先。

最近公共祖先:设rootpq的公共祖先,若其左子节点root.left和右子节点root.right都不是pq的公共祖先,则root是"最近的公共祖先"。

1.2 LCA的三种存在形式

mermaid

二、二叉搜索树(BST)的LCA问题

2.1 问题特性与解题关键

BST的左<根<右特性为LCA问题提供了捷径:

  • root.val < p.valroot.val < q.val → p,q都在右子树
  • root.val > p.valroot.val > q.val → p,q都在左子树
  • 否则,root即为LCA(包含p/q为root的情况)

2.2 方法一:迭代法(最优解)

算法流程

mermaid

代码实现

Python版本

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 优化:确保p.val <= q.val,减少判断条件
        if p.val > q.val:
            p, q = q, p
        while root:
            if root.val < p.val:  # 都在右子树
                root = root.right
            elif root.val > q.val:  # 都在左子树
                root = root.left
            else:  # 找到LCA
                break
        return root

Java版本

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (p.val > q.val) {
            TreeNode temp = p;
            p = q;
            q = temp;
        }
        while (root != null) {
            if (root.val < p.val) 
                root = root.right;
            else if (root.val > q.val) 
                root = root.left;
            else 
                break;
        }
        return root;
    }
}

C++版本

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (p->val > q->val)
            swap(p, q);
        while (root != nullptr) {
            if (root->val < p->val)
                root = root->right;
            else if (root->val > q->val)
                root = root->left;
            else
                break;
        }
        return root;
    }
};
复杂度分析
  • 时间复杂度:O(N),最坏情况为链表结构(退化为单支树)
  • 空间复杂度:O(1),仅使用常数级额外空间

2.3 方法二:递归法

算法流程

mermaid

代码实现(Python)
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root.val < p.val and root.val < q.val:
            return self.lowestCommonAncestor(root.right, p, q)
        if root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left, p, q)
        return root
复杂度分析
  • 时间复杂度:O(N),同迭代法
  • 空间复杂度:O(N),递归栈深度最坏为N

2.4 BST的LCA解法对比表

方法时间复杂度空间复杂度适用场景核心优势
迭代O(N)O(1)内存敏感场景无栈溢出风险
递归O(N)O(N)代码简洁性优先实现简单,可读性高

三、普通二叉树的LCA问题

3.1 问题难点与解决思路

普通二叉树没有BST的有序特性,需通过后序遍历(DFS) 实现回溯:

  1. 从底至顶搜索p和q
  2. 若同时在左右子树找到p和q,则当前节点为LCA
  3. 若只在一侧找到,则将找到的节点向上传递

3.2 递归回溯算法

算法流程

mermaid

代码实现

Python版本

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q:
            return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left:  # 左子树未找到,返回右子树结果
            return right
        if not right:  # 右子树未找到,返回左子树结果
            return left
        return root  # 左右都找到,当前节点为LCA

Java版本

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left == null) return right;
        if (right == null) return left;
        return root;
    }
}

C++版本

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == nullptr || root == p || root == q) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left == nullptr) return right;
        if (right == nullptr) return left;
        return root;
    }
};
复杂度分析
  • 时间复杂度:O(N),需遍历所有节点
  • 空间复杂度:O(N),递归栈深度最坏为N(链表结构)

3.3 算法正确性证明

采用反证法证明算法有效性:

  1. 假设存在更近的公共祖先X
  2. X必在root的左或右子树中
  3. 此时算法会在X处检测到左右子树同时存在p和q
  4. 与假设矛盾,故root为最近公共祖先

四、实战应用与扩展

4.1 核心算法模板总结

BST的LCA迭代模板

def bst_lca_iterative(root, p, q):
    if p.val > q.val:
        p, q = q, p
    while root:
        if root.val < p.val:
            root = root.right
        elif root.val > q.val:
            root = root.left
        else:
            return root
    return None

普通二叉树LCA递归模板

def tree_lca_recursive(root, p, q):
    if not root or root == p or root == q:
        return root
    left = tree_lca_recursive(root.left, p, q)
    right = tree_lca_recursive(root.right, p, q)
    return root if left and right else left or right

4.2 扩展习题与解题提示

  1. 二叉树的LCA II(含父指针)

    • 提示:转化为求两个链表的第一个公共节点
  2. N叉树的LCA

    • 提示:将左右子树判断扩展为遍历所有子节点
  3. LCA的批量查询优化

    • 提示:预处理树为欧拉序列+ST表,实现O(1)查询
  4. 带权树的LCA(求路径和)

    • 提示:记录节点深度和到根节点距离,distance(p,q) = dist(p) + dist(q) - 2*dist(lca)
  5. 二叉搜索树的LCA III(含重复值)

    • 提示:修改判断条件为(root.val - p.val) * (root.val - q.val) <= 0

4.3 项目实战:LeetCode-Book中的LCA问题

LeetCode-Book项目中关于LCA的完整实现位于:

  • 二叉搜索树:sword_for_offer/docs/剑指 Offer 68 - I.md
  • 普通二叉树:sword_for_offer/docs/剑指 Offer 68 - II.md

完整代码可通过以下命令获取:

git clone https://gitcode.com/GitHub_Trending/le/LeetCode-Book

五、总结与升华

5.1 知识体系梳理

mermaid

5.2 工程化思考

在实际开发中选择LCA算法时需考虑:

  1. 树的类型:BST优先选迭代法(O(1)空间)
  2. 树的规模:大型树需考虑栈溢出风险(递归深度限制)
  3. 查询频率:高频查询场景需预处理(如ST表、二进制 lifting)

5.3 学习建议

掌握LCA问题的三个阶段:

  1. 基础阶段:熟练实现BST和普通二叉树的标准解法
  2. 优化阶段:理解各种优化算法的适用场景
  3. 扩展阶段:将LCA思想应用于图论、路径规划等领域

本文代码均来自LeetCode-Book开源项目,遵循Apache 2.0开源协议。如需深入学习,建议结合项目中提供的单元测试和调试用例进行实践。收藏本文,下次遇到LCA问题,你就是面试中的解题高手!

【免费下载链接】LeetCode-Book 《剑指 Offer》 Python, Java, C++ 解题代码,LeetBook《图解算法数据结构》配套代码仓 【免费下载链接】LeetCode-Book 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Book

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值