简体   繁体   English

如何在有向图Java中打印每个循环?

[英]How to print each cycle in a directed graph Java?

I'm in a super trouble. 我很麻烦。 I really don't know how to modify the code to print each cycle that has been found. 我真的不知道如何修改代码以打印找到的每个循环。 Actually the code below is returning if the graph contains a cycle, but I also want to know what are all the possible cycles. 实际上,如果图形包含一个循环,则下面的代码将返回,但是我也想知道所有可能的循环是什么。

For example, the following graph contains three cycles 0->2->0, 0->1->2->0 and 3->3, so your function must return true. 例如,下图包含三个循环0-> 2-> 0、0-> 1-> 2-> 0和3-> 3,因此您的函数必须返回true。

// A Java Program to detect cycle in a graph
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

class Graph {
    private final int V;
    private final List<List<Integer>> adj;

    public Graph(int V) 
    {
        this.V = V;
        adj = new ArrayList<>(V);

        for (int i = 0; i < V; i++)
            adj.add(new LinkedList<>());
    }

    // This function is a variation of DFSUytil() in 
    // https://www.geeksforgeeks.org/archives/18212
    private boolean isCyclicUtil(int i, boolean[] visited, boolean[] recStack) 
    {
        // Mark the current node as visited and
        // part of recursion stack
        if (recStack[i])
            return true;

        if (visited[i])
            return false;

        visited[i] = true;

        recStack[i] = true;
        List<Integer> children = adj.get(i);

        for (Integer c: children)
            if (isCyclicUtil(c, visited, recStack))
                return true;

        recStack[i] = false;

        return false;
    }

    private void addEdge(int source, int dest) {
        adj.get(source).add(dest);
    }

    // Returns true if the graph contains a 
    // cycle, else false.
    // This function is a variation of DFS() in 
    // https://www.geeksforgeeks.org/archives/18212
    private boolean isCyclic() 
    {
        // Mark all the vertices as not visited and
        // not part of recursion stack
        boolean[] visited = new boolean[V];
        boolean[] recStack = new boolean[V];

        // Call the recursive helper function to
        // detect cycle in different DFS trees
        for (int i = 0; i < V; i++)
            if (isCyclicUtil(i, visited, recStack))
                return true;

        return false;
    }

    // Driver code
    public static void main(String[] args)
    {
        Graph graph = new Graph(4);
        graph.addEdge(0, 1);
        graph.addEdge(0, 2);
        graph.addEdge(1, 2);
        graph.addEdge(2, 0);
        graph.addEdge(2, 3);
        graph.addEdge(3, 3);

        if(graph.isCyclic())
            System.out.println("Graph contains cycle");
        else
            System.out.println("Graph doesn't "
                                + "contain cycle");
    }
}

Thank you so much. 非常感谢。

Edit: Previously I mentioned the possibility to use dfs instead of bfs , however using dfs might produce non-minimal cycles. 编辑:之前我提到了使用dfs代替bfs的可能性,但是使用dfs可能会产生非最小周期。 (eg if a cycle A->B->C->A exists and a cylce A->B->A exists, it might find the longer one first and it won't find the second one as nodes are only visited once). (例如,如果存在循环A-> B-> C-> A,并且存在循环A-> B-> A,则它可能会先找到较长的一个,而不会找到第二个,因为节点仅被访问一次)。

As per definition an elementary cycle is one where a node does not repeat itself (besides the starting one), so the case is a bit different. 按照定义, elementary cycle是一个节点不重复自身的elementary cycle (起始节点除外),因此情况略有不同。 As the questioner (of the bounty @ExceptionHandler ) wanted those cycles excluded from the output, using bfs solves that issue. 由于提问者(赏金@ExceptionHandler )想要从输出中排除那些循环,因此使用bfs解决了该问题。

For a pure (brute-force) elementary cycle search a different path finding algorithm would be required. 对于纯(强力) elementary cycle搜索,将需要不同的路径查找算法。


A general purpose (aka brute force) implementation would entail the following steps: 通用(又称蛮力)实施将需要以下步骤:

  1. For every node n of a directed graph g 对于有向图g的每个节点n
    find all pathes (using bfs ) back to n. 找到所有路径(使用bfs )回到n。

    If muliple edges between two nodes (with the same direction) exist they can be ignored at this step, as the algorithm itself should work on nodes rather than edges. 如果两个节点(方向相同)之间存在多条边,则在此步骤中可以忽略它们,因为算法本身应在节点而不是边上工作。 Multiple edges can be reintroduced into the cycles during step 5. 可以在步骤5中将多个边重新引入循环中。

  2. if no pathes are found, continue in Step 1 with n+1 如果找不到路径,请继续执行步骤1,并添加n + 1

  3. Every identified path is a cylce 每个确定的路径都是一个循环
    add them to a list of cycles, and continue with Step 1 and n+1 将它们添加到循环列表中,然后继续执行步骤1和n + 1

  4. After all nodes have been processed a list containing all possible cycles have been found (including permutations). 处理完所有节点后,将找到包含所有可能循环的列表(包括排列)。 Subcycles could not have been formed as every node can only be visited once during bfs . 子周期不可能形成,因为每个节点在bfs期间只能被访问一次。

    In this step all permutations of previously identified are grouped in sets. 在此步骤中,将先前识别的所有排列组合在一起。 Only one cylce per set is considered. 每套仅考虑一个球笼。 This can be done by ordering the node and removing duplicates. 这可以通过对节点进行排序并删除重复项来完成。

  5. Now the minimum set of cycles has been identified and can be printed out. 现在,已经确定了最小的一组循环并可以将其打印出来。
    In case you are looking for edge-specific cycles, replace the connection between two nodes with their respective edge(s). 如果您正在寻找特定于边的循环,则用两个节点各自的边替换两个节点之间的连接。


Example for the graph A->B B->C C->D D->C C->A : A->B B->C C->D D->C C->A示例:

Step 1-3: node A
path identified: A,B,C (A->B B->C C->A)
Step 1-3: node B
path identified: B,C,A (B->C C->A A->B)
Step 1-3: node C
path identified: C,A,B (C->A A->B B->C)
path identified: C,D (C->D D->C)
Step 1-3: node D
path identified: D,C (D->C C->D)
Step 4:
Identified as identical after ordering:

Set1:
A,B,C (A->B B->C C->A)
B,C,A (B->C C->A A->B)
C,A,B (C->A A->B B->C)

Set2:
C,D (C->D D->C)
D,C (D->C C->D)

Therefore remaining cycles:
A,B,C (A->B B->C C->A)
C,D (C->D D->C)
Step 5:
Simply printing out the cycles
(Check the bracket expressions for that,
 I simply added them to highlight the relevant edges).

A more efficient sample implementation to identify elementary cycles can be found here , which was directly taken from this answer . 可以在here找到更有效的示例方法来识别基本循环,该示例直接从此answer If someone wants to come up with a more detailed explanation how that algorithm works exactly feel free to do so. 如果有人想提出更详细的解释,那么该算法的工作原理完全可以自由选择。

Modifing the main method to: 将主要方法修改为:

public static void main(String[] args) {
    String nodes[] = new String[4];
    boolean adjMatrix[][] = new boolean[4][4];

    for (int i = 0; i < 4; i++) {
        nodes[i] = String.valueOf((char) ('A' + i));
    }

    adjMatrix[0][1] = true;
    adjMatrix[1][2] = true;
    adjMatrix[2][3] = true;
    adjMatrix[3][2] = true;
    adjMatrix[2][0] = true;

    ElementaryCyclesSearch ecs = new ElementaryCyclesSearch(adjMatrix, nodes);
    List cycles = ecs.getElementaryCycles();
    for (int i = 0; i < cycles.size(); i++) {
        List cycle = (List) cycles.get(i);
        for (int j = 0; j < cycle.size(); j++) {
            String node = (String) cycle.get(j);
            if (j < cycle.size() - 1) {
                System.out.print(node + " -> ");
            } else {
                System.out.print(node + " -> " + cycle.get(0));
            }
        }
        System.out.print("\n");
    }
}

leeds to the desired output of: 实现以下目标的期望输出:

A -> B -> C -> A
C -> D -> C

Donald B. Johnson paper that describes the approach in more detail can be found here . here可以找到有关该方法的详细信息的Donald B. Johnson论文。


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM