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

题意理解:

        二叉树的最近公共祖先: 简单理解,就是p和q值的那两个节点,不断向上返回,然后会在一个点汇合,那么他们第一次汇合的这个点就是他们的最近公共祖先。

解题的思路就是:

        如果这一层找到了p或q的值,就向上一层返回。

        如果父节点的左右分别找了p、q节点,则返回父节点

        如果父节点占p、q中的一个,然后子树中找到一个,则返回父节点

        否则返回null

解题方法:

        其实就是树的话,一般都可以用递归和迭代两种方法的,就是这道题,用迭代比较难理解一点。

1.递归

先判断子树是否找到p、q值,再判断中间节点是否需要返回,所以处理顺序为: 左子树、右子树、中间节点——后续遍历

对于这道题来说,递归的思路看起来更为明确。

 /**
     * 什么是最近公共祖先?
     *      两个值节点合并到一棵树的那个祖先节点
     * 题目限制:树中没有重复值,p,q一定存在,这使得题目简单了不少
     * 思路:找到一个p|q,则向上返回,当遇到某个节点,左子树,右子树返回都不为null时,
     * 说明该节点是最近祖先,向上返回,
     * 最终返回最近祖先
     * @param root
     * @param p
     * @param q
     * @return
     */
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //由于题目限制,root一定不为null
        //由于需要对左右子树进行判断,然后对中间节点进行处理,故使用后序遍历:左右中
        //左处理
        TreeNode leftResult=root.left!=null?lowestCommonAncestor(root.left,p,q):null;
        //右处理
        TreeNode rightResult=root.right!=null?lowestCommonAncestor(root.right,p,q):null;
        //中间处理
        if(root.val==p.val||root.val==q.val) return root;
        //左右结果均不为空,则说明左右子树各找到一个目标值
        //  因为pq一定存在,p!=q,且树中没有重复值,故上述结论一定成立
        if(Objects.nonNull(leftResult)&&Objects.nonNull(rightResult)) return root;
        //左右找到一个,即该子树仅存在p|q中一个,继续向上返回
        else if(Objects.nonNull(leftResult)&&Objects.isNull(rightResult)) return leftResult;
        else if(Objects.isNull(leftResult)&&Objects.nonNull(rightResult)) return rightResult;
        //左右都没有找到,则说明该子树没有目标值,返回null
        else return null;


        /**
         *   //中间处理逻辑精简
         *         if(root.val==p.val||root.val==q.val||(Objects.nonNull(leftResult)&&Objects.nonNull(rightResult))) return root;
         *             //左右找到一个,即该子树仅存在p|q中一个,继续向上返回
         *         else if(Objects.nonNull(leftResult)&&Objects.isNull(rightResult)) return leftResult;
         *         else if(Objects.isNull(leftResult)&&Objects.nonNull(rightResult)) return rightResult;
         *             //左右都没有找到,则说明该子树没有目标值,返回null
         *         else return null;
         */

    }

2.迭代

不建议用迭代,理解太复杂,除非有要求。

这里需要两个栈,一个栈用来维护遍历树的操作。

另一个栈用来描述找到的节点在树种的位置关系,用来向上返回找到的值,且可以用来判断找到的两个节点是否在此处汇合。

eg:   null null null 3 9 null 即可表示要找的值在同一层,一个null表示即将出现一个中间节点,两值汇合后的下一个null出现时对应的中间节点,即为汇合点,即最近公共祖先。

//迭代
    public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
        //自定义栈-模拟递归
        Stack stack=new Stack<>();
        //暂存找到的值,并指示层级
        Stack resultStack=new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur=stack.pop();
            if (Objects.nonNull(cur)) {
                //节点入栈
                stack.push(cur);
                stack.push(null);
                resultStack.push(null);//每加一个null相当于一层,用于只是层级
                //保证后序遍历的顺序入栈
                if(Objects.nonNull(cur.right)) stack.push(cur.right);
                if(Objects.nonNull(cur.left)) stack.push(cur.left);
            }else{
                //碰到中间节点
                cur=stack.pop();
                //该层有找到的值加入结果暂存层
                if(cur.val==p.val||cur.val==q.val){
                    resultStack.push(cur);
                }
                TreeNode node=resultStack.pop();
                if(node!=null){//该层有找到的值
                    if(resultStack.peek()!=null) return cur;//该层找到两个值
                    else {
                        resultStack.pop();//该层找到一个值,上移
                        resultStack.push(node);
                    }
                }
            }
        }
        return null;
    }

3.分析

时间复杂度:

        递归:O(n)

        迭代:O(n)

空间复杂度:

        递归:O(n)

        迭代:O(2n)

你可能感兴趣的:(刷题训练营,算法,数据结构,leetcode)