二叉树的最近公共祖先

 题目:

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

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

对示例的理解

各种情况 

解题代码

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;//根节点为空,不存在公共祖先,也就不存在最近公共祖先x
        }
        if(root == p || root == q){
            return root;//根节点为p,q中的一个,由题意可知,此时的root节点就为最近公共祖先x  即x为p,q的一个节点,为图中最右边的情况
        }
        /*接下来root不等于p和q,但是root的左树找到一个,root的右树找到一个,p和q分布在root的左右,
        此时的root节点就为最近公共祖先x,    即x为当前的根节点 为图中最左边的情况*/
        TreeNode leftRet = lowestCommonAncestor(root.left,p,q);//在root的左树找
        TreeNode rightRet= lowestCommonAncestor(root.right,p,q); //在root的右树找
        if(leftRet != null && rightRet != null){
            return root;//为图中最左边的情况
        }else if(leftRet != null){//p 和 q 均存在于给定的二叉树中。
            return leftRet;//为图中中间的情况
        }else if(rightRet != null){
            return rightRet;//为图中中间的情况
        }
        //代码走到这步,说明左右都是null,左树没有找到p和q,右树也没有找到p和q,返回null
        return null;//不过根据题中的提示 p 和 q 均存在于给定的二叉树中这句话,说明代码不可能走到这步,写这句代码更多是从java的语法规范出发,不写会出现汇编异常
    }
}

运行结果

但是我们可以用到栈(stack)这个数据结构来实现这个思想,把x到p的节点放到stack1,把x到q的节点放到stack2.

按照求相交链表的节点来做,栈的长度长的先弹出长度差个节点,再两个栈依次同时弹出一个节点,

stack1和stack2弹出的节点相同时,即该节点就是本题所求的最近公共祖先节点X

难点:怎样把从根节点到指定节点路径上所有的节点找到并放到栈里面了

         换句话说是把其他无关节点在递归回退的时候弹出去

思路:采用前序遍历对每个节点进行判断,在找到节点p和节点q的过程中,先将节点压入栈中(压栈),如果该节点的左右节点都为null,都返回false,说明该节点不是x到节点p或者节点q路径上的节点,为其他无关节点,将该节点弹出栈,如果该节点就是节点p或者节点q,递归开始回退.

注:找节点p的时候用stack1存储,找节点q的时候用stack2存储,需要用到两个栈面

代码很好的用到了链表的特性,并与之前的知识进行了结合,编程思想很厉害,很牛叉!!! 

代码

class Solution{
 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //1.创建两个栈,用来存储数据
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        //2.stack1和stack2开始存储x到节点p和x到节点q路径上的节点
        getPath(root, p, stack1);
        getPath(root, q, stack2);
        //3.判断栈的大小,并进行相关的出栈处理
        int lenStack1 = stack1.size();
        int lenStack2 = stack2.size();
        if (lenStack1 > lenStack2) {
            int len = lenStack1 - lenStack2;
            while (len != 0) {
                stack1.pop();
                len--;
            }
        } else {
            int len = lenStack2 - lenStack1;
            while (len != 0) {
                stack2.pop();
                len--;
            }
        }
        //此时stack1和stack2的数据个数,也就是节点个数相同(本质上存放的是节点的地址,所以可以用==或者!=进行判断)
        //开始查找最近的公共祖先节点x
        while (!stack1.isEmpty() && !stack2.isEmpty()) {
            if (stack1.peek() != stack2.peek()) {
                stack1.pop();
                stack2.pop();
            } else {
                return stack1.peek();//找到了最近的公共祖先节点x
            }
        }
        //此时,栈都出栈完了,为空了,还没找到,返回null
        return null;
    }
        public static boolean getPath(TreeNode root, TreeNode findNode, Stack<TreeNode> stack) {
        if (root == null || findNode == null) {
            return false;
        }
        stack.push(root);//放入根节点
        //放完之后,检查,判断当前节点是不是要查找的节点p或者q
        if (root == findNode) {
            return true;//找到了,返回true,递归开始回退
        }
        boolean retLeft = getPath(root.left, findNode, stack);//root的左边查找findNode节点
        if (retLeft == true) {
            return true;
        }
        boolean retRight = getPath(root.right, findNode, stack);//root的左边没找到,去root的右边查找findNode节点
        if (retRight == true) {
            return true;
        }
        //如果代码执行到这,说明该节点不是findNode,左边和右边也没有,即该节点不是x到节点p或者节点q路径上的节点,为其他无关节点,将该节点弹出栈
        stack.pop();//该节点被弹出栈
        return false;
    }
}

 运行结果

完结撒花✿✿ヽ(°▽°)ノ✿✿

题目链接

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/submissions/

  • 35
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值