LC236-中-二叉树的最近公共祖先

1 题目

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

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

最近公共祖先的定义为:对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。就是说最近祖先深度最大,高度最低,举例上最接近要目标点

在这里插入图片描述

在这里插入图片描述

2 思路

刚开始的思路是找目标节点的所有公共节点,找到祖先节点后,加入列表中,从后向前遍历,最有一个相同的就是最近公共祖先节点

那如何找一个节点的所有父亲节点呢?需要二叉树自下向上遍历【回溯】,那么就需要使用二叉树的后序遍历

后序遍历:在子树中招目标节点,如果找到,就将节点放入List中

class Solution {

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode> nodes1 = new LinkedList<>();
        List<TreeNode> nodes2 = new LinkedList<>();
        findAncestor(root, p, nodes1);
        findAncestor(root, q, nodes2);

        if (nodes1.isEmpty() || nodes2.isEmpty()) return null;
        TreeNode prev = null;
        int i = nodes1.size() - 1;
        int j = nodes2.size() - 1;
        while (i >= 0 && j >= 0) {
            if (nodes1.get(i).val != nodes2.get(j).val) {
                return prev;
            }
            prev = nodes1.get(i);
            i--;
            j--;
        }
        return prev;
    }

    public TreeNode findAncestor(TreeNode root, TreeNode target, List<TreeNode> nodes) {
        if (root != null) {
            TreeNode left = findAncestor(root.left, target, nodes);
            TreeNode right = findAncestor(root.right, target, nodes);

            if (left != null) {  // 左孩子中存在
                nodes.add(root);
                return left;
            }
            if (right != null) {
                nodes.add(root);
                return right;
            }
            if (root.val == target.val) {
                nodes.add(root);
                return root;
            }

        }
        return null;
    }
}

Note:
找祖先节点的方法:

  • 使用后序遍历,在左右子树中查找
  • 找祖先节点时,如何判断当前节点是否祖先节点,即在左子树和右子树中找到了目标节点,则当前节点是祖先节点;孩子节点要一直向上返回
  • 找祖先时,因为祖先节点有可能是本身,所以第一个将自己加进去,并返回孩子节点

2 优化:使用后序遍历找公共节点

接上:
找祖先节点只判断当前节点是不是一个目标节点的祖先节点。是不是可以在左右子树中同时找孩子节点,如果找到则其为最近公共节点(因为是自底向上查找的)

class Solution {

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root != null) {
            TreeNode left = lowestCommonAncestor(root.left, p, q);
            TreeNode right = lowestCommonAncestor(root.right, p, q);

            // 找到祖先节点
            if (left != null && right != null) return root;  // 最近公共祖先节点
            // 找目标节点
            if (root.val == p.val || root.val == q.val) return root;  // 孩子节点
            if (left != null) return left;
            if (right != null) return right;
        }
        return null;
    }
}

Note:

  • 与找所有的祖先节点类似,以找到孩子节点作为判断当前节点是否是祖先
  • 如果两个孩子都找到了,则当前节点就是最近公共祖先,直接返回(核心)

找公共祖先是二叉树局部扫描还是全局扫描?

【全局扫描】

TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);

在分支处没有提前返回,需要判断两个分时的情况,因此需要全部扫描完二叉树。然后根据左右的情况再返回。

局部扫描的代码

TreeNode left = lowestCommonAncestor(root.left, p, q);
if (left...) return left;
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (right...) return right;

ACM输入输出

class TreeNode{

    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) {this.val = val; }

}

// Solution

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
//        Solution solution = new Solution();
        while (in.hasNext()) {

            String[] strNums = in.nextLine().split(" ");
            List<TreeNode> nodes = new LinkedList<>();
            for (String strNum: strNums) {
                if (!strNum.isEmpty()) {
                    if (strNum.equals("null")) {
                        nodes.add(null);
                    } else {
                        nodes.add(new TreeNode(Integer.parseInt(strNum)));
                    }
                }
            }

            TreeNode root = constructTree(nodes);

            int num1 = Integer.parseInt(in.nextLine());
            int num2 = Integer.parseInt(in.nextLine());
            TreeNode p = new TreeNode(num1);
            TreeNode q = new TreeNode(num2);

            TreeNode r = solution.lowestCommonAncestor(root, p, q);
            if (r != null) System.out.println(r.val);
            else System.out.println("not found!");

        }

    }

    public static TreeNode constructTree(List<TreeNode> nodes) {
        if (!nodes.isEmpty()) {
            TreeNode node = nodes.remove(0);
            if (node != null) {
                node.left = constructTree(nodes);
                node.right = constructTree(nodes);
            }
            return node;
        }
        return null;
    }
}
/*
test case:
3 5 6 null null 2 7 null null 4 null null 1 0 null null 8
5
6
r = [5]

3 5 6 null null 2 7 null null 4 null null 1 0 null null 8
5
1
r = [3]

 */


参考

https://www.programmercarl.com/0236.二叉树的最近公共祖先.html

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值