📌  相关文章
📜  二叉树中任意数量节点的最小公祖(1)

📅  最后修改于: 2023-12-03 15:06:19.819000             🧑  作者: Mango

二叉树中任意数量节点的最小公祖

在二叉树中,我们可以定义两个节点之间的最小公祖(lowest common ancestor,LCA)为同时是这两个节点的祖先节点中深度最深的节点。

当我们需要在二叉树中查询任意数量节点的最小公祖时,可以使用以下两种方法:递归法和迭代法。

递归法

递归法的思路是从根节点开始,分别向左右子树递归查询每个节点,直到找到其中的所有节点或者找到其中一个节点。若找到其中一个节点,则返回该节点,否则继续递归查询左右子树。若在左右子树中均找到了节点,则当前节点为这些节点的最小公祖,并终止递归。

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode[] nodes) {
    if (root == null || nodes == null || nodes.length == 0) {
        return null;
    }
    if (contains(nodes, root)) {
        if (nodes.length == 1 || contains(nodes, root.left) && contains(nodes, root.right)) {
            return root;
        }
    }
    TreeNode left = lowestCommonAncestor(root.left, nodes);
    TreeNode right = lowestCommonAncestor(root.right, nodes);
    if (left != null && right != null) {
        return root;
    } else {
        return left != null ? left : right;
    }
}

private boolean contains(TreeNode[] nodes, TreeNode node) {
    for (int i = 0; i < nodes.length; i++) {
        if (nodes[i] == node) {
            return true;
        }
    }
    return false;
}

在该实现中,我们使用了一个辅助函数contains用于判断节点是否存在于一个节点数组中。在递归实现中,我们首先判断当前节点是否为目标节点之一,若是则返回当前节点。否则,我们继续查询左右子树。若左右子树中均找到了目标节点,则当前节点为这些节点的最小公祖。若只有左/右子树找到了目标节点,则继续递归查询该子树。

迭代法

迭代法的思路是使用一个哈希表来保存每个节点的父节点,然后从目标节点开始,循环向上遍历父节点,直到找到另一个目标节点或者到达根节点。同时,我们也可以将每个目标节点的深度保存在一个哈希表中,用于判断两个节点之间的深度差。

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode[] nodes) {
    if (root == null || nodes == null || nodes.length == 0) {
        return null;
    }
    Map<TreeNode, TreeNode> parentMap = new HashMap<>();
    dfs(root, null, parentMap);
    Map<TreeNode, Integer> depthMap = new HashMap<>();
    int minDepth = Integer.MAX_VALUE;
    for (TreeNode node : nodes) {
        int depth = getDepth(node, depthMap, parentMap);
        minDepth = Math.min(minDepth, depth);
    }
    while (true) {
        int count = 0;
        for (TreeNode node : nodes) {
            int depth = getDepth(node, depthMap, parentMap);
            while (depth > minDepth) {
                node = parentMap.get(node);
                depth--;
            }
            if (node == nodes[0]) {
                count++;
            }
        }
        if (count == nodes.length) {
            return nodes[0];
        }
        nodes[0] = parentMap.get(nodes[0]);
    }
}

private void dfs(TreeNode node, TreeNode parent, Map<TreeNode, TreeNode> parentMap) {
    if (node != null) {
        parentMap.put(node, parent);
        dfs(node.left, node, parentMap);
        dfs(node.right, node, parentMap);
    }
}

private int getDepth(TreeNode node, Map<TreeNode, Integer> depthMap, Map<TreeNode, TreeNode> parentMap) {
    if (node == null) {
        return 0;
    }
    if (depthMap.containsKey(node)) {
        return depthMap.get(node);
    }
    int depth = 1 + getDepth(parentMap.get(node), depthMap, parentMap);
    depthMap.put(node, depth);
    return depth;
}

在该实现中,我们首先使用一个哈希表parentMap来保存每个节点的父节点,在递归遍历二叉树时进行更新。接下来,我们使用另一个哈希表depthMap来保存每个节点的深度,用于计算两个节点之间的深度差。在循环中,我们首先计算出目标节点中深度最小的节点的深度,然后从这个节点开始,循环向上遍历父节点,每次将深度减1,直到某个节点为另一个目标节点或者到达根节点。若到达根节点也未找到,那么我们将目标节点向上移动到其父节点,然后再次进入循环。

无论是递归法还是迭代法,时间复杂度均为O(n),其中n为二叉树的节点数。