最近公共祖先(LCA)问题

本文详细解析了二叉树及二叉搜索树(BST)的最近公共祖先问题,介绍了通过后序遍历实现自下而上的查找方法,并针对BST特性提出递归与非递归两种高效解决方案。

用人不宜刻,刻则思效者去。
交友不宜滥,滥则贡谀者来。


236. 二叉树的最近公共祖先

首先我们都能想到,如果能从上到下访问就好了。这样就可以很轻松的找到公共祖先啦。
二叉树如何实现自下而上的查找呢?
回溯啊,二叉树回溯的过程就是从低到上。
后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。

这里借用代码随想录的图,便于理解整个回溯的过程。

在这里插入图片描述

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

        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
		// 左右子树返回值均为null, p和q均不在树中,即没有LCA
        if(left == null && right == null){
            return null;
        }
		//左右子树的返回值都不为null,表明p和q分别在左右子树中。即root为LCA
        if(left != null && right != null){
            return root;
        }
		// p 和 q 其中之一,在一个在左or右子树中。即不为null的就是那个LCA
        return (left != null) ? left : right;
    }
}

235. 二叉搜索树的最近公共祖先

二叉搜索树(BST) 属于二叉树,So 上面的解法 完全可以用来解决这道题。但是由于是BST,所以我们可以利用BST的性质来解决此题。

PS: 二叉搜索树(BST) 的性质:左子树的值比根节点小,右子树的值比根节点大( 左小右大)

递归解法
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || p.val == root.val || q.val == root.val){
            return root;
        }
		// 如果p,q的值都大于root的值,说明p,q都位于右子树上
        if(p.val > root.val && q.val > root.val){
            return lowestCommonAncestor(root.right,p,q);
            // 如果 p, q的值都小于root的值,说明p,q都位于左子树上。
        }else if(p.val < root.val && q.val < root.val){
            return lowestCommonAncestor(root.left,p,q);
        }else{
            return root;
        }
    }
}
非递归解法
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
     while(root != null){
         if(p.val > root.val && q.val > root.val){
             root = root.right;
         }else if(p.val < root.val && q.val < root.val){
             root = root.left;
         }else {
             return root;
         }
     }
     return root;
    }
}
### 关于最近公共祖先LCA问题的解法 #### 定义与背景 最近公共祖先(Lowest Common Ancestor, LCA),是指在一棵树中找到两个节点的最低共同父节点。这个问题在处理树形结构的数据时非常常见,在蓝桥杯竞赛以及其他编程比赛中也经常作为考察点之一。 #### 基础方法:暴力遍历 最简单的方法是从根节点开始向下逐层比较给定的两个目标节点的位置关系,直到遇到第一个能同时到达这两个节点的分支点为止。这种方法虽然直观易懂,但在大型或深层级数较多的情况下效率较低[^1]。 #### 改进方案:倍增算法 一种更高效的解决方案是采用倍增算法来求解LCA问题。此方法预先通过动态规划的方式记录下每个节点向上跳转\(2^i\)步后的祖先位置,从而可以在O(logN)时间内完成查询操作。具体步骤如下: - **预处理阶段**:对于每一个节点u及其高度h(u),计算并存储其所有可能的\(2^k\)-th父母节点parent[u][k]。 ```cpp void dfs(int u,int fa){ parent[u][0]=fa; depth[u]=depth[fa]+1; for (int i=1;(1<<i)<=depth[u];++i) parent[u][i]=parent[parent[u][i-1]][i-1]; // ...其他逻辑... } ``` - **查询阶段**:当需要寻找两节点u和v之间的LCA时,先调整两者至相同深度再逐步上移直至相遇。 ```cpp int lca_query(int u,int v){ if(depth[u]<depth[v]) swap(u,v); while(depth[u]>depth[v]){ int k=log2(depth[u]-depth[v]); u=parent[u][k]; } if(u==v)return u; for(int k=max_level;k>=0;--k){ if(parent[u][k]!=parent[v][k]){ u=parent[u][k]; v=parent[v][k]; } } return parent[u][0]; } ``` 这种基于倍增的思想不仅适用于普通的无权有向树,也可以扩展到加权边的情况,并且能够很好地满足比赛中的时间复杂度要求[^2]。 #### 应用于蓝桥杯竞赛 考虑到蓝桥杯对参赛者的基础知识掌握程度有一定要求,建议深入理解上述两种基本策略的基础上,多做练习题巩固知识点。特别是针对不同类型的输入规模优化自己的解答方式,提高程序运行速度和准确性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值