📜  使用颜色检测有向图中的循环

📅  最后修改于: 2022-05-13 01:57:54.454000             🧑  作者: Mango

使用颜色检测有向图中的循环

给定一个有向图,检查该图是否包含循环。如果给定的图形包含至少一个循环,您的函数应该返回 true,否则返回 false。

例子:

解决方案
方法:深度优先遍历可用于检测图中的循环。连通图的 DFS 生成一棵树。仅当图中存在后边时,图中才存在循环。后边是从节点到自身(自循环)或其在 DFS 生成的树中的祖先之一的边。在下图中,有 3 个后边,用叉号标记。可以观察到,这 3 个后边缘表示图中存在 3 个循环。

深度优先遍历来检测图中的循环

对于断开连接的图,我们将 DFS 森林作为输出。为了检测循环,我们可以通过检查后边来检查单个树中的循环。

图片来源: http ://www.cs.yale.edu/homes/aspnes/pinewiki/DepthFirstSearch.html
在上一篇文章中,我们讨论了将访问的顶点存储在一个单独的数组中的解决方案,该数组存储当前递归调用堆栈的顶点。

在这篇文章中,讨论了一个不同的解决方案。解决方案来自 CLRS 书。这个想法是对给定图形进行 DFS,并在进行遍历时,将以下三种颜色中的一种分配给每个顶点。

算法:

  1. 创建一个接受边缘和颜色数组的递归函数(这也可以保存为全局变量)
  2. 将当前节点标记为灰色。
  3. 遍历所有相邻节点,如果任何节点标记为灰色,则返回 true,因为循环必然存在。
  4. 如果任何相邻顶点为白色,则调用该节点的递归函数。如果函数返回true。返回真。
  5. 如果没有相邻节点是灰色的或没有返回true,则将当前节点标记为黑色并返回false。

执行:

C++
// A DFS based approach to find if there is a cycle
// in a directed graph.  This approach strictly follows
// the algorithm given in CLRS book.
#include 
using namespace std;
 
enum Color {WHITE, GRAY, BLACK};
 
// Graph class represents a directed graph using
// adjacency list representation
class Graph
{
    int V; // No. of vertices
    list* adj; // adjacency lists
 
    // DFS traversal of the vertices reachable from v
    bool DFSUtil(int v, int color[]);
public:
    Graph(int V);  // Constructor
 
    // function to add an edge to graph
    void addEdge(int v, int w);
 
    bool isCyclic();
};
 
// Constructor
Graph::Graph(int V)
{
    this->V = V;
    adj = new list[V];
}
 
// Utility function to add an edge
void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w); // Add w to v's list.
}
 
// Recursive function to find if there is back edge
// in DFS subtree tree rooted with 'u'
bool Graph::DFSUtil(int u, int color[])
{
    // GRAY :  This vertex is being processed (DFS
    //         for this vertex has started, but not
    //         ended (or this vertex is in function
    //         call stack)
    color[u] = GRAY;
 
    // Iterate through all adjacent vertices
    list::iterator i;
    for (i = adj[u].begin(); i != adj[u].end(); ++i)
    {
        int v = *i;  // An adjacent of u
 
        // If there is
        if (color[v] == GRAY)
          return true;
 
        // If v is not processed and there is a back
        // edge in subtree rooted with v
        if (color[v] == WHITE && DFSUtil(v, color))
          return true;
    }
 
    // Mark this vertex as processed
    color[u] = BLACK;
 
    return false;
}
 
// Returns true if there is a cycle in graph
bool Graph::isCyclic()
{
    // Initialize color of all vertices as WHITE
    int *color = new int[V];
    for (int i = 0; i < V; i++)
        color[i] = WHITE;
 
    // Do a DFS traversal beginning with all
    // vertices
    for (int i = 0; i < V; i++)
        if (color[i] == WHITE)
           if (DFSUtil(i, color) == true)
              return true;
 
    return false;
}
 
// Driver code to test above
int main()
{
    // Create a graph given in the above diagram
    Graph g(4);
    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 2);
    g.addEdge(2, 0);
    g.addEdge(2, 3);
    g.addEdge(3, 3);
 
    if (g.isCyclic())
        cout << "Graph contains cycle";
    else
        cout << "Graph doesn't contain cycle";
 
    return 0;
}


Java
import java.io.*;
import java.util.*;
 
class GFG
{
 
    // A DFS based approach to find if there is a cycle
    // in a directed graph. This approach strictly follows
    // the algorithm given in CLRS book.
    static int WHITE = 0, GRAY = 1, BLACK = 2;
 
    // Graph class represents a directed graph using
    // adjacency list representation
    static class Graph
    {
            int V;
            LinkedList[] adjList;
             
            // Constructor
            Graph(int ver)
            {
                V = ver;
                adjList = new LinkedList[V];
                for (int i = 0; i < V; i++)
                    adjList[i] = new LinkedList<>();
            }
    }
 
    // Utility function to add an edge
    static void addEdge(Graph g, int u, int v)
    {
            g.adjList[u].add(v); // Add v to u's list.
    }
 
    // Recursive function to find if there is back edge
    // in DFS subtree tree rooted with 'u'
    static boolean DFSUtil(Graph g, int u, int[] color)
    {
            // GRAY : This vertex is being processed (DFS
            // for this vertex has started, but not
            // ended (or this vertex is in function
            // call stack)
            color[u] = GRAY;
             
            // Iterate through all adjacent vertices
            for (Integer in : g.adjList[u])
            {
                // If there is
                if (color[in] == GRAY)
                    return true;
 
                // If v is not processed and there is a back
                // edge in subtree rooted with v
                if (color[in] == WHITE && DFSUtil(g, in, color) == true)
                    return true;
            }
 
            // Mark this vertex as processed
            color[u] = BLACK;
            return false;
    }
 
    // Returns true if there is a cycle in graph
    static boolean isCyclic(Graph g)
    {
            // Initialize color of all vertices as WHITE
            int[] color = new int[g.V];
            for (int i = 0; i < g.V; i++)
            {
                color[i] = WHITE;
            }
 
            // Do a DFS traversal beginning with all
            // vertices
            for (int i = 0; i < g.V; i++)
            {
                if (color[i] == WHITE)
                {
                    if(DFSUtil(g, i, color) == true)
                        return true;
                }
            }
            return false;
 
    }
 
    // Driver code to test above
    public static void main(String args[])
    {
            // Create a graph given in the above diagram
            Graph g = new Graph(4);
            addEdge(g, 0, 1);
            addEdge(g, 0, 2);
            addEdge(g, 1, 2);
            addEdge(g, 2, 0);
            addEdge(g, 2, 3);
            addEdge(g, 3, 3);
            if (isCyclic(g))
                System.out.println("Graph contains cycle");
            else
                System.out.println("Graph doesn't contain cycle");
    }
}
 
// This code is contributed by rachana soma


Python3
# Python program to detect cycle in
# a directed graph
 
from collections import defaultdict
 
class Graph():
    def __init__(self, V):
        self.V = V
        self.graph = defaultdict(list)
 
    def addEdge(self, u, v):
        self.graph[u].append(v)
 
    def DFSUtil(self, u, color):
        # GRAY :  This vertex is being processed (DFS
        #         for this vertex has started, but not
        #         ended (or this vertex is in function
        #         call stack)
        color[u] = "GRAY"
 
        for v in self.graph[u]:
 
            if color[v] == "GRAY":
                return True
 
            if color[v] == "WHITE" and self.DFSUtil(v, color) == True:
                return True
 
        color[u] = "BLACK"
        return False
 
    def isCyclic(self):
        color = ["WHITE"] * self.V
 
        for i in range(self.V):
            if color[i] == "WHITE":
                if self.DFSUtil(i, color) == True:
                    return True
        return False
 
# Driver program to test above functions
 
g = Graph(4)
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
print ("Graph contains cycle" if g.isCyclic() == True\
                             else "Graph doesn't contain cycle")
                              
# This program is contributed by Divyanshu Mehta


C#
// A C# program to detect cycle in
// an undirected graph using BFS.
using System;
using System.Collections.Generic;
 
class GFG
{
 
    // A DFS based approach to find if
    // there is a cycle in a directed graph.
    // This approach strictly follows the
    // algorithm given in CLRS book.
    static int WHITE = 0, GRAY = 1, BLACK = 2;
 
    // Graph class represents a directed graph
    // using adjacency list representation
    public class Graph
    {
        public int V;
        public List[] adjList;
         
        // Constructor
        public Graph(int ver)
        {
            V = ver;
            adjList = new List[V];
            for (int i = 0; i < V; i++)
                adjList[i] = new List();
        }
    }
 
    // Utility function to add an edge
    static void addEdge(Graph g, int u, int v)
    {
        g.adjList[u].Add(v); // Add v to u's list.
    }
 
    // Recursive function to find if there is back edge
    // in DFS subtree tree rooted with 'u'
    static bool DFSUtil(Graph g, int u, int[] color)
    {
        // GRAY : This vertex is being processed (DFS
        // for this vertex has started, but not
        // ended (or this vertex is in function
        // call stack)
        color[u] = GRAY;
         
        // Iterate through all adjacent vertices
        foreach (int iN in g.adjList[u])
        {
            // If there is
            if (color[iN] == GRAY)
                return true;
 
            // If v is not processed and there is a back
            // edge in subtree rooted with v
            if (color[iN] == WHITE &&
                DFSUtil(g, iN, color) == true)
                return true;
        }
 
        // Mark this vertex as processed
        color[u] = BLACK;
        return false;
    }
     
    // Returns true if there is a cycle in graph
    static bool isCyclic(Graph g)
    {
        // Initialize color of all vertices as WHITE
        int[] color = new int[g.V];
        for (int i = 0; i < g.V; i++)
        {
            color[i] = WHITE;
        }
 
        // Do a DFS traversal beginning with all
        // vertices
        for (int i = 0; i < g.V; i++)
        {
            if (color[i] == WHITE)
            {
                if(DFSUtil(g, i, color) == true)
                    return true;
            }
        }
        return false;
 
    }
 
    // Driver Code
    public static void Main(String []args)
    {
        // Create a graph given in the above diagram
        Graph g = new Graph(4);
        addEdge(g, 0, 1);
        addEdge(g, 0, 2);
        addEdge(g, 1, 2);
        addEdge(g, 2, 0);
        addEdge(g, 2, 3);
        addEdge(g, 3, 3);
        if (isCyclic(g))
            Console.WriteLine("Graph contains cycle");
        else
            Console.WriteLine("Graph doesn't contain cycle");
    }
}
 
// This code is contributed by PrinciRaj1992


Javascript


输出:

Graph contains cycle

复杂性分析:

  • 时间复杂度: O(V + E),其中 V 是顶点数,E 是图中的边数。
  • 空间复杂度: O(V)。
    因为需要一个大小为 V 的额外颜色数组。