LeetCode 236. Lowest Common Ancestor of a Binary Tree

问题描述

  • Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
    According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”
    这里写图片描述
  • 地址

问题分析

  • 求给定一棵二叉树的头节点, 以及这棵树中的两个节点p和q, 请返回这两个节点的最近的公共祖先节点。和 LeetCode 235. Lowest Common Ancestor of a Binary Search Tree 很像。只不过该题是求一颗普通二叉树的最近公共祖先
  • 首先明确:
    • 若这是一颗二叉搜索树,,若都小于root,那么递归左子树,若都大于root,递归右子树。否则,最近公共祖先为根节点
    • 若这棵树有指向父节点的指针,那么就变成了两个单向链表的相交问题,找到第一个相交节点即可。 剑指offer-两个链表中第一个公共结点
  • 方法1:DFS(先序遍历)
    • 首先是根:对于一棵树,如果根节点便是 p或者q中的一个,那么根节点便是公共祖先节点,返回即可
    • 如果递归左子树和右子树的结果都非空,那么说明,一个在左子树,一个在右子树,因为递归函数只有遇到p或者q才返回非空节点,所以最近公共祖先依旧是根节点
    • 如果递归左子树和右子树的结果,一个为空,一个不为空,那么非空递归结果即为最近公共祖先
      时间复杂度: O(N)
  • 方法2:
    • 类似于先为每一个节点设置一个指向父节点的指针,然后找两条单链表相交的第一个交点。
    • 用一个map存储节点以及该节点父节点信息,然后根据这个map可以得到从当前节点到根节点路径, 用一个set 存储从o1到根节点的路径,然后再从o2走向根节点,看路径中有没有经历已经存在set中的节点。
    • 建立map的过程为O(N),查询操作为O(logN)
  • 方法2适用于多次查询的情况,一旦建立了map,便可多次进行查询。关于这个问题,还有其他离线算法的解法。

代码实现

  • DFS
    /*
     * lowestAncestor(TreeNode root, TreeNode o1, TreeNode o2):
     *  在根节点为head树上,寻找 o1和o2的最近祖先节点
     * 初始递归,o1与o2一定在根节点为head的树上,后来可能不是
     * 所以,当该树为空时,直接返回null,当根节点为o1或o2时,直接返回根节点.否则,递归查找左子树和递归查找右子树,
     * 结果分别为 leftAncestor 和 rightAncestor,如果两者都非空,说明整棵树的最近祖先节点为根节点。
     * 如果两者有一不空,返回不空的那个,如果两者都空,返回null
     * 
     */

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        if (p == root || q == root) {
            return root;
        }
        TreeNode leftAncestor = lowestCommonAncestor(root.left, p, q);
        TreeNode rightAncestor = lowestCommonAncestor(root.right, p, q);
        if (leftAncestor != null && rightAncestor != null) {
            return root;
        }
        return leftAncestor == null ? rightAncestor : leftAncestor;
    }
  • 方法2
    /*
     * 解法二:
     *  用一张map存储父节点信息:key存node,value存node的父节点,根节点的父节点为null
     *  然后根据这张map就可以找从某个节点到根节点的路径。从某种意义上起到了指向父节点指针的作用。
     *  我们可以用一个set o1Path存储从o1到根节点的路径,然后再从o2走向根节点,看路径中有没有经历已经存在在o1Path中的节点
     *  若有,则该节点是o1与o2的最近祖先节点
     *  
     *  tips:如果有指向父节点的指针,那么该问题就转化为了两个单链表的第一个交点问题。求交点问题,有多种方法,最简单的是用额外空间
     */
     public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        Record record = new Record(root);
        return record.query(p, q);
    }

class Record {
    private HashMap<TreeNode, TreeNode> map;
    public Record(TreeNode root) {
        map = new HashMap<>();
        if (root != null) {
            map.put(root, null);
        }
        setMap(root);
    }

    private void setMap(TreeNode root) {
        if (root == null) {
            return;
        }
        if (root.left != null) {
            map.put(root.left, root);
        }
        if (root.right != null) {
            map.put(root.right, root);
        }
        setMap(root.left);
        setMap(root.right);
    }

    public TreeNode query(TreeNode p, TreeNode q) {
        HashSet<TreeNode> p2Root = new HashSet<>();
        TreeNode curNode = p;
        while (curNode != null) {
            p2Root.add(curNode);
            curNode = map.get(curNode); 
        }
        curNode = q;
        while (! p2Root.contains(curNode)) {
            curNode = map.get(curNode);
        }
        return curNode;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值