LeetCode - 二叉树的最近公共祖先

目录

题目:

示例一

示例二

示例三

提示

LeetCode链接

题目分析:

代码实现

小结:


题目:

🎈给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

🎈百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例一

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例二

 

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例三

输入:root = [1,2], p = 1, q = 2

输出:1

提示

🤨树中节点数目在范围 [2, 105] 内。
🤨-109 <= Node.val <= 109
🤨所有 Node.val 互不相同 。
🤨p != q
🤨p 和 q 均存在于给定的二叉树中。

LeetCode链接

236. 二叉树的最近公共祖先 - 力扣(LeetCode)icon-default.png?t=M85Bhttps://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/

题目分析:

🪖根据题目要求,求得两个节点的最近公共祖先。我们将这两个节点最为起点和终点,将它们和最近公共祖先连接在一起,其实就是一个链表求交点。

🪖从根节点开始,到这两个节点之间的路径是唯一的。我们可以利用两个栈,分别存储这两个路径上所有节点的地址。由于这两个路径的长度不一定相等,我们先让元素个数大的栈出掉两个路径之间元素个数的差值,这样就可以保证两个栈的元素个数是相等的。根据题目提示,所有节点的val值都不一致,然后让两个栈同时出元素,它们肯定可以出到元素的val值相等的时候(栈不一定为空),这个节点就是最近的公共祖先。

🪖接下来就是要找到这两个路径,由于无法一次性就能确定这两个路径,我们采取回溯算法(深度优先遍历)。先让每个节点都入栈,然后递归去判断这个节点左右子树,如果左右子树都没有找到目标节点,那么说明这个节点就不是这个路径上的。直接将这个节点弹出。由于是递归的算法,其实在判断节点时,是从下往上去遍历的,所以叫做深度优先遍历。我们在脑海中要能想到这样递归的一个过程。

代码实现

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) {
            return null;
        }
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        //获得路径
        getPass(root, p, stack1);
        getPass(root, q, stack2);
        //求差值,多的路径弹出
        if(stack1.size() > stack2.size()) {
            int differ = stack1.size() - stack2.size();
            while(differ-- != 0) {
                stack1.pop();
            }
        }else {
            int differ = stack2.size() - stack1.size();
            while(differ-- != 0) {
                stack2.pop();
            }
        }
        //同时出,相等则为结果
        while(stack1.peek() != stack2.peek()) {
            stack1.pop();
            stack2.pop();
        }
        return stack1.pop();

    }
    //深度优先遍历,直接入,如果左右孩子都不是,
    //则它不为路径上的节点,弹出改节点即可。
    private boolean getPass(TreeNode root, TreeNode tmp, Stack<TreeNode> stack) {
        
        if(root == null || tmp == null) {
            return false;
        }
        stack.push(root);
        
        if(root == tmp) {
            return true;
        }
        
        boolean ret1 = getPass(root.left, tmp, stack);
        if(ret1 == true) {
            return true;
        }
        boolean ret2 = getPass(root.right, tmp, stack);
        if(ret2 == true) {
            return true;
        }
        stack.pop();
        return false;

    }
}

小结:

🐵回溯算法(深度优先遍历),是相当巧妙的。就是往下去走,一直走,发现不对了就往回退,然后再往下走,直到走到对为止。我们在脑海中要能想到这样递归的过程。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小太空人w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

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

抵扣说明:

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

余额充值