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

题目:给定一颗二叉树,要求找到该树种两个指定的结点的最近公共祖先。

解题思路

求结点的祖先结点,那么结点必然需要一层层向上遍历,但结点的指针只有左孩子和右孩子,也就无法向上遍历。因此我们需要用一种数据结构存储每个结点的直接父亲结点,这样遍历到当前结点则可以直接获取其父亲结点。这种数据结构我采用Map,我们可以对二叉树做一次遍历,无论哪种,只需要保证可以遍历完整颗二叉树即可,每遍历一个结点,我们将当前结点作为key,其直接父亲结点作为value

当遍历完整颗二叉树的时候就可以得到每个节点对应其父亲结点的集合,注意,根节点是没有父亲结点的,因此我们需要设置根节点的父亲结点为其自身。之后对于给定的结点o1,我们就可以一步步向上游走,将得到的父亲结点进行保存,直到遍历到根节点。而对另一个结点02,我们则可以以同样的思路向上遍历,但每次遍历查询o2的父亲结点是否在o1的祖先集合中,可得代码如下:

public Node process(Node head, Node o1, Node o2){
        HashMap<Node, Node> map = new HashMap<>();
        setFatherNode(head, map);
        map.put(head, head);
        HashSet<Node> set = new HashSet<>();
        Node cur = o1;
        set.add(o1);
        while(cur!=map.get(cur)){
            set.add(map.get(cur));
            cur = map.get(cur);
        }
        cur = o2;
        while(cur!=map.get(cur)){
            if(set.contains(cur)){
                return cur;
            }
            cur = map.get(cur);
        }
        return head;
    }
    public void setFatherNode(Node head, HashMap<Node, Node> map){
        if(head==null){
            return;
        }
        map.put(head.left, head);
        map.put(head.right, head);
        setFatherNode(head.left, map);
        setFatherNode(head.right, map);
    }

思路较为清晰,但代码有点难想。我们可以换一种思路,两个结点的公共祖先无非只有三种情况:

  1. o1o2的祖先。
  2. o2o1的祖先。
  3. o1o2共同祖先。

那么我们可以直接遍历整棵树,分为左子树和右子树遍历,分别从左子树和右子树中找o1o2,如果两边都找到了,则必定公共祖先为根节点,如果左边或者右边有一边为空,则必然是上面的第一种或者第二种情况,可得代码如下:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root==null||root==p||root==q) return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    if(left!=null&&right!=null) return root;
    return left!=null?left:right;
}

很巧妙只能说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码匀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值