[英]Algorithm to find lowest common ancestor in directed acyclic graph?
想象一个有向无环图如下,其中:
我可以使用什么算法来确定两个任意节点的最低共同祖先 (LCA),例如,以下的共同祖先:
笔记:
Den Roman 的链接( 存档版)似乎很有希望,但对我来说似乎有点复杂,所以我尝试了另一种方法。 这是我使用的一个简单算法:
假设您想用x和y两个节点计算 LCA(x,y)。 每个节点必须有一个值color
和count
,分别是。 初始化为白色和0 。
count
加一 count
数值设置为0 的每个红色节点都是一个解决方案。
可能有多个解决方案,具体取决于您的图表。 例如,考虑这个图:
LCA(4,5) 可能的解决方案是 1 和 2。
请注意,如果您想找到 3 个或更多节点的 LCA,它仍然有效,您只需要为每个节点添加不同的颜色。
我正在寻找相同问题的解决方案,并在以下论文中找到了解决方案:
http://dx.doi.org/10.1016/j.ipl.2010.02.014
简而言之,您不是在寻找最低的共同祖先,而是寻找他们在本文中定义的最低的 SINGLE 共同祖先。
只是一些疯狂的想法。 使用两个输入节点作为根,并逐步同时执行两个 BFS 怎么样。 在某个步骤,当它们的 BLACK 集合(记录访问过的节点)中有重叠时,算法停止,重叠的节点是它们的 LCA。 这样,任何其他共同祖先的距离都会比我们发现的要长。
假设您想在图中找到 x 和 y 的祖先。
维护一组向量-父节点(存储每个节点的父节点)。
首先做一个 bfs(保持存储每个顶点的父母)并找到 x 的所有祖先(找到 x 的父母并使用父母,找到 x 的所有祖先)并将它们存储在一个向量中。 此外,将每个父项的深度存储在向量中。
使用相同的方法找到 y 的祖先并将它们存储在另一个向量中。 现在,您有两个向量分别存储 x 和 y 的祖先以及它们的深度。
LCA 将是具有最大深度的共同祖先。 深度定义为距根(in_degree=0 的顶点)的最长距离。 现在,我们可以按深度的降序对向量进行排序并找出 LCA。 使用这种方法,我们甚至可以找到多个 LCA(如果有)。
如果图形有循环,那么“祖先”的定义是松散的。 也许你的意思是 DFS 或 BFS 树输出的祖先? 或者也许“祖先”是指有向图中最小化E
和B
跳数的节点?
如果您不担心复杂性,那么您可以计算从每个节点到E
和B
的 A* (或 Dijkstra 最短路径)。 对于可以到达E
和B
的节点,您可以找到最小化PathLengthToE + PathLengthToB
的节点。
编辑:既然你已经澄清了一些事情,我想我明白你在找什么。
如果您只能“向上”树,那么我建议您执行来自E
的 BFS 和来自B
的 BFS。 图中的每个节点都有两个与之关联的变量:来自B
跳数和来自E
跳数。 让B
和E
都有图节点列表的副本。 B
的列表按从B
的跳数排序,而E
的列表按从E
的跳数排序。
对于B
的列表中的每个元素,尝试在E
的列表中找到它。 将匹配项放在第三个列表中,按来自B
跳数 + 来自E
跳数排序。 在您用尽B
的列表之后,您的第三个排序列表应该在其头部包含 LCA。 这允许一个解决方案,多个解决方案(通过他们的 BFS 对B
排序任意选择),或者没有解决方案。
我还需要完全相同的东西,在 DAG(有向无环图)中找到 LCA。 LCA 问题与 RMQ(范围最小查询问题)有关。
可以将 LCA 减少到 RMQ,并从有向无环图中找到两个任意节点的所需 LCA。
我发现这个教程细节很好。 我也打算实施这个。
我提出 O(|V| + |E|) 时间复杂度解决方案,我认为这种方法是正确的,否则请纠正我。
给定有向无环图,我们需要找到两个顶点 v 和 w 的 LCA。
Step1:使用时间复杂度为 O(|V| + |E|) 的 bfs http://en.wikipedia.org/wiki/Breadth-first_search从根顶点找到所有顶点的最短距离,并找到每个顶点的父节点。
Step2:使用parent找到两个顶点的共同祖先,直到我们到达根顶点时间复杂度- 2|v|
Step3:LCA 将是具有最大最短距离的共同祖先。
所以,这是 O(|V| + |E|) 时间复杂度算法。
如果我错了,请纠正我,或者欢迎任何其他建议。
package FB;
import java.util.*;
public class commomAnsectorForGraph {
public static void main(String[] args){
commomAnsectorForGraph com = new commomAnsectorForGraph();
graphNode g = new graphNode('g');
graphNode d = new graphNode('d');
graphNode f = new graphNode('f');
graphNode c = new graphNode('c');
graphNode e = new graphNode('e');
graphNode a = new graphNode('a');
graphNode b = new graphNode('b');
List<graphNode> gc = new ArrayList<>();
gc.add(d);
gc.add(f);
g.children = gc;
List<graphNode> dc = new ArrayList<>();
dc.add(c);
d.children = dc;
List<graphNode> cc = new ArrayList<>();
cc.add(b);
c.children = cc;
List<graphNode> bc = new ArrayList<>();
bc.add(a);
b.children = bc;
List<graphNode> fc = new ArrayList<>();
fc.add(e);
f.children = fc;
List<graphNode> ec = new ArrayList<>();
ec.add(b);
e.children = ec;
List<graphNode> ac = new ArrayList<>();
a.children = ac;
graphNode gn = com.findAncestor(g, c, d);
System.out.println(gn.value);
}
public graphNode findAncestor(graphNode root, graphNode a, graphNode b){
if(root == null) return null;
if(root.value == a.value || root.value == b.value) return root;
List<graphNode> list = root.children;
int count = 0;
List<graphNode> temp = new ArrayList<>();
for(graphNode node : list){
graphNode res = findAncestor(node, a, b);
temp.add(res);
if(res != null) {
count++;
}
}
if(count == 2) return root;
for(graphNode t : temp){
if(t != null) return t;
}
return null;
}
}
class graphNode{
char value;
graphNode parent;
List<graphNode> children;
public graphNode(char value){
this.value = value;
}
}
每个人。 请在 Java 中尝试。
static String recentCommonAncestor(String[] commitHashes, String[][] ancestors, String strID, String strID1)
{
HashSet<String> setOfAncestorsLower = new HashSet<String>();
HashSet<String> setOfAncestorsUpper = new HashSet<String>();
String[] arrPair= {strID, strID1};
Arrays.sort(arrPair);
Comparator<String> comp = new Comparator<String>(){
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1);
}};
int indexUpper = Arrays.binarySearch(commitHashes, arrPair[0], comp);
int indexLower = Arrays.binarySearch(commitHashes, arrPair[1], comp);
setOfAncestorsLower.addAll(Arrays.asList(ancestors[indexLower]));
setOfAncestorsUpper.addAll(Arrays.asList(ancestors[indexUpper]));
HashSet<String>[] sets = new HashSet[] {setOfAncestorsLower, setOfAncestorsUpper};
for (int i = indexLower + 1; i < commitHashes.length; i++)
{
for (int j = 0; j < 2; j++)
{
if (sets[j].contains(commitHashes[i]))
{
if (i > indexUpper)
if(sets[1 - j].contains(commitHashes[i]))
return commitHashes[i];
sets[j].addAll(Arrays.asList(ancestors[i]));
}
}
}
return null;
}
这个想法很简单。 我们假设 commitHashes 按降级顺序排序。 我们找到字符串的最低和最高索引(哈希值不表示)。 很明显(考虑到后代顺序)共同祖先只能在上索引(散列中的较低值)之后。 然后我们开始枚举提交的哈希值并构建后代父链的链。 为此,我们有两个哈希集由提交的最低和最高哈希的父项初始化。 setOfAncestorsLower,setOfAncestorsUpper。 如果下一个哈希提交属于任何链(哈希集),那么如果当前索引高于最低哈希的索引,那么如果它包含在另一个集合(链)中,我们将返回当前哈希作为结果。 如果没有,我们将其父项(祖先 [i])添加到 hashset,它跟踪当前元素包含的集合的祖先集合。 这就是全部,基本上
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.