Open In App

Python Program for Detect Cycle in Graph using DSU

Last Updated : 05 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Prerequisite: Introduction to Disjoint Set

Given an undirected graph, The task is to detect if there is a cycle in the given graph.

Example:

Input: N = 4, E = 4 

Output: Yes
Explanation: The diagram clearly shows a cycle 0 to 2 to 1 to 0

Input: N = 4, E = 3

Output: No
Explanation: There is no cycle in the given graph

Python Program for Detect Cycle in Graph using DSU:

The key idea behind using DSU to detect cycles is to maintain a set for each component (connected subgraph) in the graph. As we traverse the graph, we check if the sets of the two vertices being considered are the same. If they are, it means there is a cycle because the vertices are already in the same component.

Step-by-step approach:

  • Initialization:
    • Create an array parent[] of size V, where V is the number of vertices in the graph.
    • Initialize parent[i] to i for all i. This means that initially, each vertex is its own parent.
    • Create an array rank[] of size V, where rank[i] represents the rank of the set containing vertex i.
  • Find:
    • To find the parent of a vertex x, follow these steps:
    • While parent[x] != x:
      • Set x to parent[x].
    • Return x.
  • Union:
    • To merge two sets containing vertices x and y, follow these steps:
      • Find the parents of x and y.
      • If the ranks of the sets are different, attach the set with the lower rank to the set with the higher rank.
      • If the ranks are the same, increment the rank of the set containing y.
      • Set parent[x] to y.
  • Cycle Detection:
    • Iterate through each vertex u in the graph.
    • For each adjacent vertex v of u:
      • If u and v belong to the same set, a cycle is detected.
      • Otherwise, perform union of the sets containing u and v.

Below is the implementation of the above approach:

Python3




class Solution:
    def __init__(self):
        # List to store parent of each vertex.
        self.parent = []
 
        # List to store the rank of each set
        # for union by rank.
        self.rank = []
 
    # Function to find the parent of a set
    # (with path compression).
    def find(self, x):
 
        # If the parent is the same as the vertex,
        # return the vertex.
        if self.parent[x] == x:
            return x
 
        # Path compression: Set the parent of the
        # current vertex to its root.
        self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
 
    # Function to perform union of two sets
    # (with union by rank).
    def union(self, x, y):
 
        # Find the root (parent) of the sets
        # containing x and y.
        x_parent = self.find(x)
        y_parent = self.find(y)
 
        # If sets have different ranks, attach the
        # smaller rank tree under the larger rank tree.
        if self.rank[x_parent] > self.rank[y_parent]:
            self.parent[y_parent] = x_parent
        elif self.rank[x_parent] < self.rank[y_parent]:
            self.parent[x_parent] = y_parent
        else:
            # If ranks are equal, choose any tree as
            # the parent and increment its rank.
            self.parent[x_parent] = y_parent
            self.rank[y_parent] += 1
 
    # Function to detect cycle using Disjoint Set Union
    # (DSU) in an undirected graph.
    def detectCycle(self, V, adj):
 
        # Resize and initialize the parent and rank lists.
        self.parent = [i for i in range(V)]
        self.rank = [0] * V
 
        # Initialize each vertex as the parent
        # of its own set.
        for i in range(V):
            self.parent[i] = i
 
        # Iterate through each vertex and its adjacent
        # vertices in the graph.
        for u in range(V):
            for v in adj[u]:
 
                # Process each edge only once (u < v).
                if u < v:
                    # If both vertices belong to the same set,
                    # a cycle is detected.
                    if self.find(u) == self.find(v):
                        return True
                    else:
                        # Otherwise, perform union of the sets
                        # containing u and v.
                        self.union(u, v)
 
        # No cycles detected in the graph.
        return False
 
 
# Driver code
if __name__ == "__main__":
 
    V = 4  # Number of vertices
    E = 3  # Number of edges
    adj = [[] for _ in range(V)]  # Adjacency list
 
    edges = [(1, 2), (2, 3), (3, 1)]
    for u, v in edges:
        adj[u-1].append(v-1)
        adj[v-1].append(u-1)
 
    # Create an instance of the Solution class
    sol = Solution()
 
    # Check if there is a cycle in the graph
    if sol.detectCycle(V, adj):
        print("The graph contains a cycle.")
    else:
        print("The graph does not contain a cycle.")


Output

The graph contains a cycle.

Time Complexity:

  • Initializing Disjoint Set: O(V)
  • Find Operation: O(log V) amortized
  • Union Operation: O(log V) amortized
  • Detecting Cycle: O(E * log V)

Auxiliary Space:

  • Parent and Rank Lists: O(V)
  • Other Variables: O(1)

Related Article: Detect Cycle in Graph using DSU



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads