题目:

力扣

题解一:

核心:后序遍历模型,每个父节点接收子节点的状态(是否含有p、q)并把这个状态依次向上传递,直到该结点满足祖先节点的条件,时间复杂度O(N)。

public class LowestCommonAncestor {

    private TreeNode result;

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        dfs(root, p, q);
        return result;
    }

    /**
     * 当前root子树中是否找到p或q
     *
     * @param root 根节点
     * @param p 节点p
     * @param q 节点q
     * @return true:找到p或q, false:未找到
     */
    private boolean dfs(TreeNode root, TreeNode p, TreeNode q) {
        // 如果已经找到答案,就不需要再遍历了,提前退出遍历(剪枝)
        if (result != null) {
            return false;
        }

        if (root == null) {
            return false;
        }

        // p,q 是否在左子树
        boolean inLeft = dfs(root.left, p, q);
        // p,q 是否在右子树
        boolean inRight = dfs(root.right, p, q);
        // p,q 是否为当前节点
        boolean inCurrent = root == p || root == q;

        /**
         * 1. 左右子树都找到了,证明当前root就是最近公共祖先
         * 2. 如果当前节点为p,q中的一个,只要左子树或右子树有另一个节点,那么root就是最近公共祖先
         */
        if ((inLeft && inRight) || (inCurrent && (inLeft || inRight))) {
            result = root;
        }

        return inLeft || inRight || inCurrent;
    }
}

题解二:

自底向上分别从p,q遍历到根节点并且记录路径,如果存在公共祖先,那么p,q的路径必然相交,交点即为最近的公共祖先。

1. dfs 遍历整棵二叉树,使用哈希表,记录节点的父节点。

2.利用哈希表,自底向上遍历获取p, q的路径。

3.遍历两条路径,寻找交点。


    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 节点value与父节点value哈希表
        Map<Integer, Integer> nodeToParentMap = new HashMap<>();
        // 节点value与节点哈希表
        Map<Integer, TreeNode> valueToNodeMap = new HashMap<>();

        // dfs 遍历树构建哈希表
        dfsForLowestCommonAncestor(root, null, nodeToParentMap, valueToNodeMap);

        // 获取路径
        List<Integer> pPathList = getNodePath(p, nodeToParentMap);
        List<Integer> qPathList = getNodePath(q, nodeToParentMap);

        // 遍历路径获取交点
        for (Integer nodeForPPath : pPathList) {
            for (Integer nodeForQPath : qPathList) {
                // 交点
                if (nodeForPPath.equals(nodeForQPath)) {
                    return valueToNodeMap.get(nodeForPPath);
                }
            }
        }

        return null;
    }

    /**
     * 利用哈希表自底向上获取节点路径
     *
     * @param node 当前节点
     * @param nodeToParentMap key:节点value,value:父节点value
     * @return 节点路径列表
     */
    private List<Integer> getNodePath(TreeNode node, Map<Integer, Integer> nodeToParentMap) {
        List<Integer> pathList = new ArrayList<>();
        Integer nodeValue = node.val;
        pathList.add(nodeValue);
        while (nodeToParentMap.get(nodeValue) != null) {
            Integer parentNodeValue = nodeToParentMap.get(nodeValue);

            pathList.add(parentNodeValue);
            nodeValue = parentNodeValue;
        }
        return pathList;
    }

    /**
     * dfs遍历
     *
     * @param node 当前节点
     * @param parentNode 父节点
     * @param nodeToParentMap 当前节点 -> 父节点
     * @param valueToNodeMap 当前节点value -> node
     */
    private void dfsForLowestCommonAncestor(TreeNode node, TreeNode parentNode, Map<Integer, Integer> nodeToParentMap, Map<Integer, TreeNode> valueToNodeMap) {
        if (node == null) {
            return;
        }

        dfsForLowestCommonAncestor(node.left, node, nodeToParentMap, valueToNodeMap);

        if (parentNode != null) {
            nodeToParentMap.put(node.val, parentNode.val);
        }

        valueToNodeMap.put(node.val, node);
        dfsForLowestCommonAncestor(node.right, node, nodeToParentMap, valueToNodeMap);

    }

时间复杂度:O(N^2)