GitHub_Trending/leetcode1/leetcode二叉树的最近公共祖先:递归的回溯判断

GitHub_Trending/leetcode1/leetcode二叉树的最近公共祖先:递归的回溯判断

【免费下载链接】leetcode Leetcode solutions 【免费下载链接】leetcode 项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

1. 问题本质与核心挑战

二叉树的最近公共祖先(Lowest Common Ancestor,LCA)问题是面试高频算法题,本质是寻找两个目标节点在树中最深的共同祖先节点。该问题在不同树结构下有显著差异:

  • 二叉搜索树(BST):可利用节点值大小关系实现O(h)时间复杂度(h为树高)
  • 普通二叉树:需通过后序遍历实现回溯判断,同样保持O(n)时间复杂度

本文将以GitHub_Trending/leetcode1/leetcode项目中的实现为基础,系统讲解两种场景下的递归解法。

2. 二叉搜索树(BST)的LCA解法

2.1 问题特性与关键洞察

BST的左子树节点值均小于根节点,右子树节点值均大于根节点。这一特性使LCA判断可通过以下规则实现:

  • 若两目标节点值均大于当前节点值,LCA必在右子树
  • 若两目标节点值均小于当前节点值,LCA必在左子树
  • 否则当前节点即为LCA(包含一个节点是另一个祖先的情况)

2.2 C语言实现代码深度解析

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;            // 节点值
 *     struct TreeNode *left;  // 左子节点指针
 *     struct TreeNode *right; // 右子节点指针
 * };
 */

struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    // 提取当前节点及目标节点值
    int rootVal = root->val;
    int pVal = p->val;
    int qVal = q->val;

    // 两节点均在右子树
    if (pVal > rootVal && qVal > rootVal) {
        return lowestCommonAncestor(root->right, p, q);
    } 
    // 两节点均在左子树
    else if (pVal < rootVal && qVal < rootVal) {
        return lowestCommonAncestor(root->left, p, q);
    }
    // 当前节点即为LCA
    return root;
}

2.3 执行流程可视化

mermaid

2.4 边界情况处理

场景算法行为示例
一个节点是另一个的祖先返回祖先节点p是q的父节点时返回p
节点值相等直接返回当前节点处理重复值BST时仍有效
树退化为链表自动转为线性查找保持O(n)时间复杂度

3. 普通二叉树的LCA解法

3.1 问题差异与解决方案

普通二叉树无节点值大小关系特性,需通过后序遍历实现:

  1. 递归查找左子树是否包含p或q
  2. 递归查找右子树是否包含p或q
  3. 若当前节点满足以下条件之一则为LCA:
    • 左子树和右子树各包含一个目标节点
    • 当前节点是目标节点之一且子树包含另一节点

3.2 Go语言实现代码解析

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val   int
 *     Left  *TreeNode
 *     Right *TreeNode
 * }
 */

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
    // 基准条件:空节点或找到目标节点
    if root == nil || root == p || root == q {
        return root
    }
    
    // 后序遍历
    left := lowestCommonAncestor(root.Left, p, q)  // 左子树查找结果
    right := lowestCommonAncestor(root.Right, p, q)// 右子树查找结果
    
    // 左右子树各找到一个目标节点
    if left != nil && right != nil {
        return root
    }
    
    // 返回非空的查找结果(其中一个子树包含两个目标节点)
    if left != nil {
        return left
    }
    return right
}

3.3 递归回溯过程可视化

mermaid

4. 两种算法的复杂度对比

算法类型时间复杂度空间复杂度适用场景
BST递归O(h)O(h)有序二叉树,值唯一
普通二叉树递归O(n)O(h)任意二叉树结构

注:h为树的高度,平衡树时h=logn,最坏情况(链表)h=n

5. 实战应用与扩展思考

5.1 算法优化方向

  • 迭代实现:将递归转为栈实现,避免递归栈溢出风险
  • 路径记录法:分别记录根到两节点路径,比较路径找LCA
  • 父指针法:通过哈希表存储节点父指针,类似链表相交问题

5.2 常见错误与避坑指南

  1. 空指针处理:必须先判断root==nil再访问成员
  2. 基准条件设置:正确处理"当前节点是目标节点"的情况
  3. 后序遍历顺序:确保先处理左右子树再判断当前节点

5.3 面试高频变形题

  1. 二叉树中两个节点的距离:LCA到两节点距离之和
  2. N叉树的LCA:扩展子节点查找逻辑到多叉场景
  3. 含父指针的LCA:转化为求链表第一个公共节点

6. 总结与学习建议

LCA问题是理解树递归遍历的经典案例,掌握后可迁移解决多种树相关问题。建议学习路径:

  1. 从BST的LCA入手,理解利用树特性优化的思路
  2. 掌握普通二叉树的后序遍历解法,体会回溯思想
  3. 通过迭代实现加深对递归过程的理解
  4. 尝试解决变形问题巩固知识点

GitHub_Trending/leetcode1/leetcode项目中提供了多种语言实现,建议对比学习不同语言的语法特性对算法实现的影响。

来源:GitHub_Trending/leetcode1/leetcode项目
代码路径:c/0235-lowest-common-ancestor-of-a-binary-search-tree.c
更多解法:go/0236-lowest-common-ancestor-of-binary-tree.go

【免费下载链接】leetcode Leetcode solutions 【免费下载链接】leetcode 项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

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

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

抵扣说明:

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

余额充值