📜  有向图中的欧拉电路

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

有向图中的欧拉电路

欧拉路径是图中的一条路径,它只访问每条边一次。欧拉回路是在同一顶点开始和结束的欧拉路径。

如果一个图有一个欧拉循环,则称它为欧拉图。我们已经讨论了无向图的欧拉回路。在这篇文章中,同样讨论了有向图。

例如,下图的欧拉循环为 {1, 0, 3, 4, 0, 2, 1}

SCC

如何检查有向图是否是欧拉图?
如果以下条件为真,则有向图具有欧拉循环(来源:Wiki)
1)所有非零度的顶点都属于一个单一的强连通分量。
2) 每个顶点的入度等于出度。

我们可以使用 Kosaraju 的基于 DFS 的简单算法检测单连通分量。

为了比较入度和出度,我们需要存储每个顶点的入度和出度。出度可以通过邻接表的大小来获得。度数可以通过创建一个大小等于顶点数的数组来存储。

以下是上述方法的实现。

C++
// A C++ program to check if a given directed graph is Eulerian or not
#include
#include 
#define CHARS 26
using namespace std;
 
// A class that represents an undirected graph
class Graph
{
    int V;    // No. of vertices
    list *adj;    // A dynamic array of adjacency lists
    int *in;
public:
    // Constructor and destructor
    Graph(int V);
    ~Graph()   { delete [] adj; delete [] in; }
 
    // function to add an edge to graph
    void addEdge(int v, int w) { adj[v].push_back(w);  (in[w])++; }
 
    // Method to check if this graph is Eulerian or not
    bool isEulerianCycle();
 
    // Method to check if all non-zero degree vertices are connected
    bool isSC();
 
    // Function to do DFS starting from v. Used in isConnected();
    void DFSUtil(int v, bool visited[]);
 
    Graph getTranspose();
};
 
Graph::Graph(int V)
{
    this->V = V;
    adj = new list[V];
    in = new int[V];
    for (int i = 0; i < V; i++)
       in[i] = 0;
}
 
/* This function returns true if the directed graph has a eulerian
   cycle, otherwise returns false  */
bool Graph::isEulerianCycle()
{
    // Check if all non-zero degree vertices are connected
    if (isSC() == false)
        return false;
 
    // Check if in degree and out degree of every vertex is same
    for (int i = 0; i < V; i++)
        if (adj[i].size() != in[i])
            return false;
 
    return true;
}
 
// A recursive function to do DFS starting from v
void Graph::DFSUtil(int v, bool visited[])
{
    // Mark the current node as visited and print it
    visited[v] = true;
 
    // Recur for all the vertices adjacent to this vertex
    list::iterator i;
    for (i = adj[v].begin(); i != adj[v].end(); ++i)
        if (!visited[*i])
            DFSUtil(*i, visited);
}
 
// Function that returns reverse (or transpose) of this graph
// This function is needed in isSC()
Graph Graph::getTranspose()
{
    Graph g(V);
    for (int v = 0; v < V; v++)
    {
        // Recur for all the vertices adjacent to this vertex
        list::iterator i;
        for(i = adj[v].begin(); i != adj[v].end(); ++i)
        {
            g.adj[*i].push_back(v);
            (g.in[v])++;
        }
    }
    return g;
}
 
// This function returns true if all non-zero degree vertices of
// graph are strongly connected (Please refer
// https://www.geeksforgeeks.org/connectivity-in-a-directed-graph/ )
bool Graph::isSC()
{
    // Mark all the vertices as not visited (For first DFS)
    bool visited[V];
    for (int i = 0; i < V; i++)
        visited[i] = false;
 
    // Find the first vertex with non-zero degree
    int n;
    for (n = 0; n < V; n++)
        if (adj[n].size() > 0)
          break;
 
    // Do DFS traversal starting from first non zero degrees vertex.
    DFSUtil(n, visited);
 
     // If DFS traversal doesn't visit all vertices, then return false.
    for (int i = 0; i < V; i++)
        if (adj[i].size() > 0 && visited[i] == false)
              return false;
 
    // Create a reversed graph
    Graph gr = getTranspose();
 
    // Mark all the vertices as not visited (For second DFS)
    for (int i = 0; i < V; i++)
        visited[i] = false;
 
    // Do DFS for reversed graph starting from first vertex.
    // Starting Vertex must be same starting point of first DFS
    gr.DFSUtil(n, visited);
 
    // If all vertices are not visited in second DFS, then
    // return false
    for (int i = 0; i < V; i++)
        if (adj[i].size() > 0 && visited[i] == false)
             return false;
 
    return true;
}
 
// Driver program to test above functions
int main()
{
    // Create a graph given in the above diagram
    Graph g(5);
    g.addEdge(1, 0);
    g.addEdge(0, 2);
    g.addEdge(2, 1);
    g.addEdge(0, 3);
    g.addEdge(3, 4);
    g.addEdge(4, 0);
 
    if (g.isEulerianCycle())
       cout << "Given directed graph is eulerian n";
    else
       cout << "Given directed graph is NOT eulerian n";
    return 0;
}


Java
// A Java program to check if a given directed graph is Eulerian or not
 
// A class that represents an undirected graph
import java.io.*;
import java.util.*;
import java.util.LinkedList;
 
// This class represents a directed graph using adjacency list
class Graph
{
    private int V;   // No. of vertices
    private LinkedList adj[];//Adjacency List
    private int in[];            //maintaining in degree
 
    //Constructor
    Graph(int v)
    {
        V = v;
        adj = new LinkedList[v];
        in = new int[V];
        for (int i=0; i i =adj[v].iterator();
        while (i.hasNext())
        {
            n = i.next();
            if (!visited[n])
                DFSUtil(n,visited);
        }
    }
 
    // Function that returns reverse (or transpose) of this graph
    Graph getTranspose()
    {
        Graph g = new Graph(V);
        for (int v = 0; v < V; v++)
        {
            // Recur for all the vertices adjacent to this vertex
            Iterator i = adj[v].listIterator();
            while (i.hasNext())
            {
                g.adj[i.next()].add(v);
                (g.in[v])++;
            }
        }
        return g;
    }
 
    // The main function that returns true if graph is strongly
    // connected
    Boolean isSC()
    {
        // Step 1: Mark all the vertices as not visited (For
        // first DFS)
        Boolean visited[] = new Boolean[V];
        for (int i = 0; i < V; i++)
            visited[i] = false;
 
        // Step 2: Do DFS traversal starting from the first vertex.
        DFSUtil(0, visited);
 
        // If DFS traversal doesn't visit all vertices, then return false.
        for (int i = 0; i < V; i++)
            if (visited[i] == false)
                return false;
 
        // Step 3: Create a reversed graph
        Graph gr = getTranspose();
 
        // Step 4: Mark all the vertices as not visited (For second DFS)
        for (int i = 0; i < V; i++)
            visited[i] = false;
 
        // Step 5: Do DFS for reversed graph starting from first vertex.
        // Starting Vertex must be same starting point of first DFS
        gr.DFSUtil(0, visited);
 
        // If all vertices are not visited in second DFS, then
        // return false
        for (int i = 0; i < V; i++)
            if (visited[i] == false)
                return false;
 
        return true;
    }
 
    /* This function returns true if the directed graph has a eulerian
       cycle, otherwise returns false  */
    Boolean isEulerianCycle()
    {
        // Check if all non-zero degree vertices are connected
        if (isSC() == false)
            return false;
 
        // Check if in degree and out degree of every vertex is same
        for (int i = 0; i < V; i++)
            if (adj[i].size() != in[i])
                return false;
 
        return true;
    }
 
    public static void main (String[] args) throws java.lang.Exception
    {
        Graph g = new Graph(5);
        g.addEdge(1, 0);
        g.addEdge(0, 2);
        g.addEdge(2, 1);
        g.addEdge(0, 3);
        g.addEdge(3, 4);
        g.addEdge(4, 0);
 
        if (g.isEulerianCycle())
            System.out.println("Given directed graph is eulerian ");
        else
            System.out.println("Given directed graph is NOT eulerian ");
    }
}
//This code is contributed by Aakash Hasija


Python3
# A Python3 program to check if a given
# directed graph is Eulerian or not
 
from collections import defaultdict
 
class Graph():
 
    def __init__(self, vertices):
        self.V = vertices
        self.graph = defaultdict(list)
        self.IN = [0] * vertices
 
    def addEdge(self, v, u):
 
        self.graph[v].append(u)
        self.IN[u] += 1
 
    def DFSUtil(self, v, visited):
        visited[v] = True
        for node in self.graph[v]:
            if visited[node] == False:
                self.DFSUtil(node, visited)
 
    def getTranspose(self):
        gr = Graph(self.V)
 
        for node in range(self.V):
            for child in self.graph[node]:
                gr.addEdge(child, node)
 
        return gr
 
    def isSC(self):
        visited = [False] * self.V
 
        v = 0
        for v in range(self.V):
            if len(self.graph[v]) > 0:
                break
 
        self.DFSUtil(v, visited)
 
        # If DFS traversal doesn't visit all
        # vertices, then return false.
        for i in range(self.V):
            if visited[i] == False:
                return False
 
        gr = self.getTranspose()
 
        visited = [False] * self.V
        gr.DFSUtil(v, visited)
 
        for i in range(self.V):
            if visited[i] == False:
                return False
 
        return True
 
    def isEulerianCycle(self):
 
        # Check if all non-zero degree vertices
        # are connected
        if self.isSC() == False:
            return False
 
        # Check if in degree and out degree of
        # every vertex is same
        for v in range(self.V):
            if len(self.graph[v]) != self.IN[v]:
                return False
 
        return True
 
 
g = Graph(5);
g.addEdge(1, 0);
g.addEdge(0, 2);
g.addEdge(2, 1);
g.addEdge(0, 3);
g.addEdge(3, 4);
g.addEdge(4, 0);
if g.isEulerianCycle():
   print( "Given directed graph is eulerian");
else:
   print( "Given directed graph is NOT eulerian");
 
# This code is contributed by Divyanshu Mehta


C#
// A C# program to check if a given
// directed graph is Eulerian or not
 
// A class that represents an
// undirected graph
using System;
using System.Collections.Generic;
 
// This class represents a directed
// graph using adjacency list
class Graph{
     
// No. of vertices
public int V;  
 
// Adjacency List
public List []adj;
 
// Maintaining in degree
public int []init;          
 
// Constructor
Graph(int v)
{
    V = v;
    adj = new List[v];
    init = new int[V];
     
    for(int i = 0; i < v; ++i)
    {
        adj[i] = new List();
        init[i]  = 0;
    }
}
 
// Function to add an edge into the graph
void addEdge(int v, int w)
{
    adj[v].Add(w);
    init[w]++;
}
 
// A recursive function to print DFS
// starting from v
void DFSUtil(int v, Boolean []visited)
{
     
    // Mark the current node as visited
    visited[v] = true;
 
    // Recur for all the vertices
    // adjacent to this vertex
    foreach(int i in adj[v])
    {
         
        if (!visited[i])
            DFSUtil(i, visited);
    }
}
 
// Function that returns reverse
// (or transpose) of this graph
Graph getTranspose()
{
    Graph g = new Graph(V);
    for(int v = 0; v < V; v++)
    {
         
        // Recur for all the vertices
        // adjacent to this vertex
        foreach(int i in adj[v])
        {
            g.adj[i].Add(v);
            (g.init[v])++;
        }
    }
    return g;
}
 
// The main function that returns
// true if graph is strongly connected
Boolean isSC()
{
     
    // Step 1: Mark all the vertices
    // as not visited (For first DFS)
    Boolean []visited = new Boolean[V];
    for(int i = 0; i < V; i++)
        visited[i] = false;
 
    // Step 2: Do DFS traversal starting
    // from the first vertex.
    DFSUtil(0, visited);
 
    // If DFS traversal doesn't visit
    // all vertices, then return false.
    for(int i = 0; i < V; i++)
        if (visited[i] == false)
            return false;
 
    // Step 3: Create a reversed graph
    Graph gr = getTranspose();
 
    // Step 4: Mark all the vertices as
    // not visited (For second DFS)
    for(int i = 0; i < V; i++)
        visited[i] = false;
 
    // Step 5: Do DFS for reversed graph
    // starting from first vertex.
    // Staring Vertex must be same
    // starting point of first DFS
    gr.DFSUtil(0, visited);
 
    // If all vertices are not visited
    // in second DFS, then return false
    for(int i = 0; i < V; i++)
        if (visited[i] == false)
            return false;
 
    return true;
}
 
// This function returns true if the
// directed graph has a eulerian
// cycle, otherwise returns false 
Boolean isEulerianCycle()
{
     
    // Check if all non-zero degree
    // vertices are connected
    if (isSC() == false)
        return false;
 
    // Check if in degree and out
    // degree of every vertex is same
    for(int i = 0; i < V; i++)
        if (adj[i].Count != init[i])
            return false;
 
    return true;
}
 
// Driver code
public static void Main(String[] args)
{
    Graph g = new Graph(5);
    g.addEdge(1, 0);
    g.addEdge(0, 2);
    g.addEdge(2, 1);
    g.addEdge(0, 3);
    g.addEdge(3, 4);
    g.addEdge(4, 0);
     
    if (g.isEulerianCycle())
        Console.WriteLine("Given directed " +
                          "graph is eulerian ");
    else
        Console.WriteLine("Given directed " +
                          "graph is NOT eulerian ");
}
}
 
// This code is contributed by Princi Singh


Javascript


输出:

Given directed graph is eulerian 

上述实现的时间复杂度是 O(V + E),因为 Kosaraju 的算法需要 O(V + E) 时间。在运行 Kosaraju 的算法后,我们遍历所有顶点并比较需要 O(V) 时间的度数和度数。

请参阅以下内容作为此应用程序。
查找给定的字符串数组是否可以链接形成一个圆圈。