如何在有向图中检测一个循环,其中一个接一个地添加边。 如果添加最新边缘导致循环创建,则不应添加该边缘。
对于上述问题,什么是最佳解决方案?
答案 0 :(得分:1)
在定向图中,假设您要从u添加边到v:
您必须检查v和u是否已连接。您可以使用BFS或DFS来执行此操作。
如果您确定从v到u有连接,那么从u到v添加边将导致循环。
对于Demo,我从互联网上实现了图形,并添加了路径是否存在于v到u的逻辑。此逻辑在以下代码中处于函数doesPathExistBetween(V from, V to)
中。
package java8new;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
public class Digraph<V> {
public static class Edge<V> {
private V vertex;
private int cost;
public Edge(V v, int c) {
vertex = v;
cost = c;
}
public V getVertex() {
return vertex;
}
public int getCost() {
return cost;
}
@Override
public String toString() {
return "Edge [vertex=" + vertex + ", cost=" + cost + "]";
}
}
/**
* A Map is used to map each vertex to its list of adjacent vertices.
*/
private Map<V, List<Edge<V>>> neighbors = new HashMap<V, List<Edge<V>>>();
/**
* String representation of graph.
*/
public String toString() {
StringBuffer s = new StringBuffer();
for (V v : neighbors.keySet())
s.append("\n " + v + " -> " + neighbors.get(v));
return s.toString();
}
/**
* Add a vertex to the graph. Nothing happens if vertex is already in graph.
*/
public void add(V vertex) {
if (neighbors.containsKey(vertex))
return;
neighbors.put(vertex, new ArrayList<Edge<V>>());
}
/**
* Add an edge to the graph; if either vertex does not exist, it's added.
* This implementation allows the creation of multi-edges and self-loops.
*/
public void add(V from, V to, int cost) {
this.add(from);
this.add(to);
neighbors.get(from).add(new Edge<V>(to, cost));
}
public List<V> outboundNeighbors(V vertex) {
List<V> list = new ArrayList<V>();
for (Edge<V> e : neighbors.get(vertex))
list.add(e.vertex);
return list;
}
public void bfs(V root) {
// Since queue is a interface
Queue<V> queue = new LinkedList<V>();
Map<V, Boolean> visited = new HashMap<V, Boolean>();
if (root == null)
return;
visited.put(root, Boolean.TRUE);
// Adds to end of queue
queue.add(root);
while (!queue.isEmpty()) {
// removes from front of queue
V r = queue.remove();
System.out.println(r.toString());
// Visit child first before grandchild
for (V n : outboundNeighbors(r)) {
if (!visited.containsKey(n)) {
queue.add(n);
visited.put(n, Boolean.TRUE);
}
}
}
}
public Boolean doesPathExistBetween(V from, V to) {
Queue<V> queue = new LinkedList<V>();
Map<V, Boolean> visited = new HashMap<V, Boolean>();
visited.put(from, Boolean.TRUE);
// Adds to end of queue
queue.add(from);
while (!queue.isEmpty()) {
// removes from front of queue
V r = queue.remove();
// Visit child first before grandchild
for (V n : outboundNeighbors(r)) {
if (!visited.containsKey(n)) {
if (n.equals(to))
return Boolean.TRUE;
queue.add(n);
visited.put(n, Boolean.TRUE);
}
}
}
return Boolean.FALSE;
}
public static void main(String[] args) throws IOException {
Digraph<Integer> graph = new Digraph<Integer>();
// Adding vertices
graph.add(0);
graph.add(1);
graph.add(2);
graph.add(3);
// Adding Edges with weights
graph.add(0, 1, 1);
graph.add(1, 2, 2);
graph.add(3, 0, 2);
System.out.println("Path betwen 0 to 2 exists : " + graph.doesPathExistBetween(0, 2));
System.out.println("Path betwen 1 to 3 exists : " + graph.doesPathExistBetween(1, 3));
graph.bfs(0);
graph.bfs(1);
}
}
答案 1 :(得分:0)
然后直截了当的算法就像:
现在给出状态X(当前状态)的图形不具有循环,它们在顶点A - >之间添加新边缘时。 B可以创建一个新循环。但在这种情况下,A将始终是这种循环的一部分。因此,从A重新开始遍历是有意义的。根据图表的大小和性质,您可以更快地找到它。
答案 2 :(得分:0)
这个问题的算法仍然是研究的主题,但是对于许多实际情况,当你添加边(https://en.wikipedia.org/wiki/Dominator_(graph_theory))时,维护图的支配树有很大帮助。
维护支配树可以按每次添加操作分摊O(log(V + E))时间,或者可能稍好一些。
添加边时仍然需要执行DFS或BFS,但由于循环中的所有顶点都经过相同的直接支配器,因此您只需要搜索将成为同一父节点的子节点的顶点,并且该父节点本身,在支配树中为了检测那个循环。
如果在构建图形时不允许循环,则很可能会有很多支配者,并且搜索每个边缘插入的节点数量会很小。
答案 3 :(得分:0)
如果您正在为 Kruskal 的算法编写代码以查找图的最小生成树,那么您将遇到此用例。 您必须根据权重对所有边进行排序,然后逐一选择以形成图形。对于每条边,你必须检查它是否形成任何循环。
public boolean isThisEdgeFormingLoop(Edge n) {
int parent=0 ;int rank =1;
int fromParent=parentRankMatrix[parent][n.getFrom()];
int toParent=parentRankMatrix[parent][n.getTo()];
if(fromParent==-1 && toParent==-1) {
if(parentRankMatrix[rank][n.getTo()] > parentRankMatrix[rank][n.getFrom()]) {
parentRankMatrix[parent][n.getFrom()]=n.getTo();
}else if(parentRankMatrix[rank][n.getTo()] < parentRankMatrix[rank][n.getFrom()]) {
parentRankMatrix[parent][n.getTo()]=n.getFrom();
}else {
parentRankMatrix[rank][n.getTo()]++;
parentRankMatrix[parent][n.getFrom()]=n.getTo();
}
return false;
}else {
int absFromParent=getAbsoluteParent(n.getFrom());
int absToParent=getAbsoluteParent(n.getTo());
if(absFromParent==absToParent) {
return true;
}else {
if(parentRankMatrix[rank][absFromParent] > parentRankMatrix[rank][absToParent]) {
parentRankMatrix[parent][absToParent]=absFromParent;
}else if(parentRankMatrix[rank][absFromParent] < parentRankMatrix[rank][absToParent]){
parentRankMatrix[parent][absFromParent]=absToParent;
}else {
parentRankMatrix[rank][absToParent]++;
parentRankMatrix[parent][absFromParent]=absToParent;
}
return false;
}
}
}
完整的实现链接和视频说明:
https://github.com/Sandip9021/Ds-Algo/tree/master/src/kruskal