📅  最后修改于: 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为二叉树的节点数。