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

113 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

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

解题思路

求结点的祖先结点,那么结点必然需要一层层向上遍历,但结点的指针只有左孩子和右孩子,也就无法向上遍历。因此我们需要用一种数据结构存储每个结点的直接父亲结点,这样遍历到当前结点则可以直接获取其父亲结点。这种数据结构我采用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;
}

很巧妙只能说。