Java 检测循环有向图
Java Detecting a cyclic directed Graph
我目前正在尝试编写一个程序来检查有向图是否是循环的。我不确定我做错了什么(很可能我做错了所有事情,所以请 Whosebug 告诉我我的愚蠢!)。如果我能提供任何帮助,我将不胜感激,因为我已经到了我不知道可能是什么问题的地步。
输入是一个邻接表,例如:
0: 2 4
1: 2 4
2: 3 4
3: 4
4: 0 1 2 3
(0 指向 2 和 4;1 指向 2 和 4,依此类推...)
我的想法是检查我正在检查的节点是否 'grey'(部分探索)。如果是,则它必须是后向边,因此是循环图。黑色边缘总是被探索或交叉边缘,所以这不应该触发循环消息。我的目标是进行深度优先搜索
如果 A-->B 和 B-->A,这不应触发有关循环的消息(但 A--> B,B-->C,C-->A 应该)。
hasCycle 调用 hasCycleInSubgraph,后者通过图的邻接列表递归调用自身。
class qs {
private ArrayList<Integer>[] adjList;
private Stack<Integer> stack;
private ArrayList<Integer> whiteHat;
private ArrayList<Integer> greyHat;
private ArrayList<Integer> blackHat;
public qs(ArrayList<Integer>[] graph) {
this.adjList = graph;
this.stack = new Stack();
this.whiteHat = new ArrayList<Integer>();
this.greyHat = new ArrayList<Integer>();
this.blackHat = new ArrayList<Integer>();
for (Integer h = 0; h < adjList.length; h++) {
whiteHat.add(h);
}
}
public boolean hasCycle() {
for (Integer i = 0; i < adjList.length; i++) {
// System.out.print("Local root is: ");
// System.out.println(i);
whiteHat.remove(i);
greyHat.add(i);
if (hasCycleInSubgraph(i) == true) {
return true;
}
greyHat.remove(i);
blackHat.add(i);
}
return false;
}
public boolean hasCycleInSubgraph(Integer inp) {
if (blackHat.contains(inp)) {
return false;
}
for (Integer branch : adjList[inp]) {
// System.out.print("Adj is: ");
// System.out.println(branch);
if ( greyHat.contains(branch) && !inp.equals(branch) ) {
return true;
}
whiteHat.remove(branch);
greyHat.add(branch);
if ( hasCycleInSubgraph(branch) == true ) {
return true;
}
greyHat.remove(branch);
blackHat.add(branch);
}
return false;
}
}
你把它复杂化了:可以通过深度优先搜索来检测循环:从任何给定节点,步行到每个连接的节点;如果你回到一个已经访问过的节点,你就有了一个循环。
class qs {
private final ArrayList<Integer>[] graph;
qs(ArrayList<Integer>[] graph) {
this.graph = graph;
}
boolean hasCycle() {
List<Integer> visited = new ArrayList<>();
for (int i = 0; i < graph.length; ++i) {
if (hasCycle(i, visited)) {
return true;
}
}
}
private boolean hasCycle(int node, List<Integer> visited) {
if (visited.contains(node)) {
return true;
}
visited.add(node);
for (Integer nextNode : graph[node]) {
if (hasCycle(nextNode, visited)) {
return true;
}
}
visited.remove(visited.length() - 1);
return false;
}
}
如果要检测比给定长度更长的循环,只需检查递归的深度:
if (visited.contains(node) && visited.size() > 2) {
请注意,除了堆栈中的内容外,这不需要保留任何状态。依赖可变状态会使代码线程不安全(例如,两个线程同时调用 hasCycle
会相互干扰),因此应该避免 - 即使您不希望使用代码现在以多线程的方式,它避免了问题。
我目前正在尝试编写一个程序来检查有向图是否是循环的。我不确定我做错了什么(很可能我做错了所有事情,所以请 Whosebug 告诉我我的愚蠢!)。如果我能提供任何帮助,我将不胜感激,因为我已经到了我不知道可能是什么问题的地步。
输入是一个邻接表,例如:
0: 2 4
1: 2 4
2: 3 4
3: 4
4: 0 1 2 3
(0 指向 2 和 4;1 指向 2 和 4,依此类推...)
我的想法是检查我正在检查的节点是否 'grey'(部分探索)。如果是,则它必须是后向边,因此是循环图。黑色边缘总是被探索或交叉边缘,所以这不应该触发循环消息。我的目标是进行深度优先搜索
如果 A-->B 和 B-->A,这不应触发有关循环的消息(但 A--> B,B-->C,C-->A 应该)。
hasCycle 调用 hasCycleInSubgraph,后者通过图的邻接列表递归调用自身。
class qs {
private ArrayList<Integer>[] adjList;
private Stack<Integer> stack;
private ArrayList<Integer> whiteHat;
private ArrayList<Integer> greyHat;
private ArrayList<Integer> blackHat;
public qs(ArrayList<Integer>[] graph) {
this.adjList = graph;
this.stack = new Stack();
this.whiteHat = new ArrayList<Integer>();
this.greyHat = new ArrayList<Integer>();
this.blackHat = new ArrayList<Integer>();
for (Integer h = 0; h < adjList.length; h++) {
whiteHat.add(h);
}
}
public boolean hasCycle() {
for (Integer i = 0; i < adjList.length; i++) {
// System.out.print("Local root is: ");
// System.out.println(i);
whiteHat.remove(i);
greyHat.add(i);
if (hasCycleInSubgraph(i) == true) {
return true;
}
greyHat.remove(i);
blackHat.add(i);
}
return false;
}
public boolean hasCycleInSubgraph(Integer inp) {
if (blackHat.contains(inp)) {
return false;
}
for (Integer branch : adjList[inp]) {
// System.out.print("Adj is: ");
// System.out.println(branch);
if ( greyHat.contains(branch) && !inp.equals(branch) ) {
return true;
}
whiteHat.remove(branch);
greyHat.add(branch);
if ( hasCycleInSubgraph(branch) == true ) {
return true;
}
greyHat.remove(branch);
blackHat.add(branch);
}
return false;
}
}
你把它复杂化了:可以通过深度优先搜索来检测循环:从任何给定节点,步行到每个连接的节点;如果你回到一个已经访问过的节点,你就有了一个循环。
class qs {
private final ArrayList<Integer>[] graph;
qs(ArrayList<Integer>[] graph) {
this.graph = graph;
}
boolean hasCycle() {
List<Integer> visited = new ArrayList<>();
for (int i = 0; i < graph.length; ++i) {
if (hasCycle(i, visited)) {
return true;
}
}
}
private boolean hasCycle(int node, List<Integer> visited) {
if (visited.contains(node)) {
return true;
}
visited.add(node);
for (Integer nextNode : graph[node]) {
if (hasCycle(nextNode, visited)) {
return true;
}
}
visited.remove(visited.length() - 1);
return false;
}
}
如果要检测比给定长度更长的循环,只需检查递归的深度:
if (visited.contains(node) && visited.size() > 2) {
请注意,除了堆栈中的内容外,这不需要保留任何状态。依赖可变状态会使代码线程不安全(例如,两个线程同时调用 hasCycle
会相互干扰),因此应该避免 - 即使您不希望使用代码现在以多线程的方式,它避免了问题。