Javatpoint Logo
Javatpoint Logo

Detect Cycle in Graph using DSU in C++

In this article, you will learn about how to detect cycle in Graph using DSU in C++ with severa examples.

Graph:

A graph is a collection of nodes (vertices) and edges connecting pairs of nodes. Graphs can be directed or undirected and may have weights assigned to edges.

Cycle:

A cycle in an undirected graph is a sequence of vertices where the first and last vertices are the same, and the other vertices are distinct.

In a directed graph, a cycle is a sequence of vertices where there is an edge from each vertex to the next, and the last vertex has an edge to the first vertex.

Cycle Detection:

Detecting cycles in a graph is important for diverse packages, which include ensuring the integrity of records structures and preventing infinite loops in algorithms.

Cycles can be detected using various algorithms and approaches, each suitable for different kinds of graphs and scenarios.

Detecting cycles in a graph, which is a fundamental hassle in graph concept and computer technology. A cycle in a graph is a sequence of vertices and edges that begins and ends at the same vertex. Detecting cycles is essential for various programs, including ensuring the consistency of dependencies in software, detecting deadlocks in concurrent systems, and identifying circular references in databases.

Detecting cycles in a graph is a traditional hassle, and various techniques can be used to tackle it. Here are a few different methods to detect cycles in a graph:

1. Depth-First Search (DFS):

Depth-First Search (DFS) is an effective graph traversal Algorithm that explores as deeply as feasible alongside each branch before backtracking. When utilized to detect cycles in a graph, DFS, in aggregate with Disjoint Set Union (DSU), efficiently manages sets of vertices. The key to cycle detection involves monitoring the parent of each vertex in the course of the traversal.

In the context of DFS and DSU, the Algorithm maintains a parent array to maintain a record of the parent vertex for each vertex visited. As DFS explore the graph, it backtracks when necessary, and the Algorithm updates its parent information. The core of cycle detection is recognizing that a cycle is detected if a previous visit to a vertex during DFS occurs and it is not the parent of the current vertex.

The Disjoint Set Union data structure enhances the efficiency of set operations, enabling quick checks for vertex connectivity and aiding in the identification of cycles during the DFS traversal. By combining the expressive capabilities of DFS with the efficiency of DSU, the algorithm provides a robust solution for detecting cycles in graphs.

This approach effectively leverages the nature of DFS to traverse the graph and the structure of DSU to manage sets, making it a reliable method for cycle detection in various applications, such as network analysis, circuit design, and more.

Let's go through the implementation of DFS with DSU in C++ to detect cycles:

Output:

The graph contains a cycle.

Explanation:

Graph Representation:

The Graph class represents an undirected graph using an adjacency list. It has methods to add edges to the graph.

DSU (Disjoint Set Union) Implementation:

The DSU class is a Disjoint Set Union data structure. It has methods for the find operation with path compression and the unionSets operation with rank optimization.

DFS Function for Cycle Detection:

  • The isCyclicDFS function is a depth-first search (DFS) function. It takes a vertex v, tracks visited vertices (visited), and uses a stack (inStack) to detect cycles during the DFS traversal.
  • It recursively explores adjacent vertices, and if it encounters an already visited vertex that is not the parent of the current vertex, it indicates the presence of a cycle.

Main Cycle Detection Function:

  • The isCyclic function initializes data structures, including the DSU, and iterates through all vertices.
  • For each unvisited vertex, it calls isCyclicDFS to perform DFS and check for cycles. If a cycle is found, the function returns true; otherwise, it returns false.

Main Function:

  • In the main function, a sample graph is created with four vertices, and edges are added to create a cycle (0 -> 1 -> 2 -> 3 -> 0).
  • The isCyclic function is called, and the program prints whether the graph contains a cycle or not.

Output:

If the graph contains a cycle, the output will indicate that the graph has a cycle. In this example, it will print "The graph contains a cycle."

The overall idea is to use DFS to traverse the graph and use DSU to efficiently check for cycles. The DSU data structure helps in keeping track of sets of vertices and detecting cycles during the traversal. The code elegantly combines graph representation, DSU implementation, and DFS to efficiently detect cycles in an undirected graph.

The DSU is employed to keep track of sets and facilitate cycle detection during the DFS traversal. This modular and well-organized code provides a clear understanding of the cycle detection process in graphs using the Reverse Delete Algorithm.

Complexity Analysis:

Time Complexity:

DFS Function (isCyclicDFS):

The DFS function visits each vertex and each edge once.

The time complexity of DFS is O(V + E), where V is the number of vertices and E is the number of edges.

Main Cycle Detection Function (isCyclic):

Calls isCyclicDFS for each unvisited vertex, and isCyclicDFS has a time complexity of O(V + E).

The overall time complexity of the isCyclic function is O(V + E).

Overall Complexity:

Time Complexity: O(V + E)

Space Complexity:

Graph Representation:

The space complexity for the graph is O(V + E), where V is the number of vertices and E is the number of edges due to the adjacency list representation.

DSU Data Structure:

The DSU data structure uses O(V) space to store the parent and rank arrays.

DFS Function (isCyclicDFS):

The space complexity for the DFS function is O(V) due to the recursion stack (inStack and visited arrays).

Main Cycle Detection Function (isCyclic):

The overall space complexity of the isCyclic function is dominated by the space required for the graph and DSU.

The total space complexity is O(V + E).

Overall Complexity:

Space Complexity: O(V + E)

These complexities provide a general understanding of the efficiency of the algorithm in terms of time and space requirements. The time complexity is linear with respect to the number of vertices and edges, making it suitable for graphs of moderate size. The space complexity is also reasonable, considering the need to store the graph and DSU data structures.

2. Breadth-First Search (BFS):

Breadth-First Search (BFS) stands as a graph traversal Algorithm that systematically explores vertices level by level, prioritizing the visitation of neighbors before delving into deeper levels. This Algorithm is particularly versatile and adaptable to various graph-related tasks. When applied to cycle detection in an undirected graph, BFS can be paired with the Disjoint Set Union (DSU) data structure to achieve efficient and reliable results.

In the context of cycle detection, BFS differs from Depth-First Search (DFS) in its exploration strategy. While DFS uses a stack to keep track of vertices in the current path, BFS employs a queue to systematically explore vertices level by level. This distinction makes BFS well-suited for applications where a breadth-first exploration is preferred.

To adapt BFS for cycle detection using DSU, the algorithm maintains a DSU structure to efficiently manage sets of vertices and their connectivity. As BFS traverses the graph, it systematically processes vertices in a level-wise manner, enqueuing neighbors before exploring deeper levels. The DSU aids in determining whether adding an edge between two vertices would form a cycle by efficiently checking the connectivity of the vertices.

The process involves iteratively dequeuing vertices, exploring their neighbors, and updating the DSU structure based on the connectivity information. If, at any point, BFS encounters a previous visit vertex that is not the parent of the current vertex, a cycle is detected. The DSU ensures that these connectivity checks are performed efficiently.

The combination of BFS and DSU for cycle detection offers a different perspective and strategy compared to DFS-based approaches. The use of a queue in BFS provides a systematic and level-wise exploration, making it suitable for certain scenarios where breadth-first exploration is advantageous. When coupled with the efficiency of DSU, this approach becomes a powerful tool for detecting cycles in undirected graphs.

Below is an explanation of how BFS can be used to detect cycles with DSU in C++:

Output:

The graph contains a cycle.

Explanation:

let's break down the provided C++ code for using Breadth-First Search (BFS) to detect cycles with Disjoint Set Union (DSU):

1. Graph Representation:

The Graph class represents an undirected graph using an adjacency list.

Edges are added to the graph using the addEdge method.

2. DSU Implementation:

  • The DSU class is a Disjoint Set Union data structure.
  • It includes methods for the find operation with path compression and the unionSets operation with rank optimization.

3. BFS Function for Cycle Detection (isCyclicBFS):

  • This function performs BFS traversal starting from a source vertex (src).
  • It maintains a queue (q) for BFS traversal, a visited array to mark visited vertices, and a parent array to keep track of the parent of each vertex during traversal.
  • If a visited vertex is encountered and is not the parent of the current vertex, and the vertices do not belong to the same set in the DSU, a cycle is detected.

4. Main Cycle Detection Function (isCyclic):

  • The isCyclic function initializes the required data structures, including the DSU.
  • It iterates through all vertices, calling the isCyclicBFS function for each unvisited vertex.
  • If a cycle is found in any connected component, the function returns true; otherwise, it returns

5. Main Function:

  • In the main function, a sample graph is created with four vertices, and edges are added to create a cycle (0 -> 1 -> 2 -> 3 -> 0).
  • The isCyclic function is called, and the program prints whether the graph contains a cycle or not.

6. Output:

  • If the graph contains a cycle, the output will indicate that the graph has a cycle. In this example, it will print "The graph contains a cycle."
  • This approach leverages BFS for efficient traversal and DSU for cycle detection, making it suitable for graphs with cycles and moderate sizes.

Complexity Analysis:

Time Complexity:

BFS Function (isCyclicBFS):

The BFS function explores each vertex and edge once.

The time complexity of BFS is O(V + E), where V is the number of vertices and E is the number of edges.

Main Cycle Detection Function (isCyclic):

Calls isCyclicBFS for each unvisited vertex, and isCyclicBFS has a time complexity of O(V + E).

The overall time complexity of the isCyclic function is O(V + E).

Overall Complexity:

Time Complexity: O(V + E)

Space Complexity:

Graph Representation:

The space complexity for the graph is O(V + E) due to the adjacency list representation.

DSU Data Structure:

The DSU data structure uses O(V) space to store the parent and rank arrays.

BFS Function (isCyclicBFS):

The space complexity for the BFS function is O(V) due to the queue (q), visited, and parent arrays.

Main Cycle Detection Function (isCyclic):

The overall space complexity of the isCyclic function is dominated by the space required for the graph and DSU.

The total space complexity is O(V + E).

Overall Complexity:

Space Complexity: O(V + E)

These complexities provide a general understanding of the efficiency of the algorithm in terms of time and space requirements. The time complexity is linear with respect to the number of vertices and edges, making it suitable for graphs of moderate size. The space complexity is also reasonable, considering the need to store the graph and DSU data structures.

3. Union-Find (Disjoint Set Union - DSU):

The Union-Find data structure, commonly known as Disjoint Set Union (DSU), is a versatile and efficient tool used for managing sets of elements and performing operations like union and find. This data structure finds widespread applications in various algorithms, especially in graph-related problems, including cycle detection.

DSU plays a crucial role in efficiently managing disjoint sets of vertices and determining whether adding an edge between two vertices would create a cycle in an undirected graph. The core idea behind DSU is to represent each disjoint set as a rooted tree, where each node points to its parent. The structure maintains information about the parent of each element and is optimized for quick union and find operations.

The find operation in DSU is used to determine the representative (root) of the set to which a particular element belongs. During this operation, path compression is often employed to flatten the tree, reducing the height of the tree and improving the efficiency of subsequent find operations.

The union operation is utilized to merge two disjoint sets. To maintain balance and prevent the tree from becoming skewed, a rank or size heuristic is employed. This ensures that the tree remains relatively balanced, contributing to the overall efficiency of the data structure.

In the context of cycle detection in an undirected graph, DSU excels in keeping track of connected components and efficiently determining whether adding an edge between two vertices would create a cycle. If the two vertices belong to the same set (i.e., have the same root), adding an edge between them would introduce a cycle. DSU's efficiency in these operations makes it particularly suitable for applications where rapid checks for connectivity and cycle formation are essential.

By leveraging DSU in cycle detection algorithms, one can achieve a balance between simplicity and efficiency, making it a preferred choice in scenarios where real-time responsiveness is crucial, such as in network analysis, circuit design, and various optimization problems.

Here's how Union-Find (DSU) is utilized to detect cycles in an undirected graph in C++:

Output:

The graph contains a cycle.

Explanation:

let's break down the provided C++ code for using Union-Find (Disjoint Set Union - DSU) to detect cycles in an undirected graph:

1. Graph Representation:

  • The Graph class represents an undirected graph using an edge list (edges).
  • Edges are added to the graph using the addEdge method, where each edge is represented as a pair of vertices.

2. DSU Implementation:

  • The DSU class is a Disjoint Set Union data structure.
  • It includes methods for the find operation with path compression and the unionSets operation with rank optimization.

3. Union-Find Operations:

Find Operation (find):

It recursively finds the root (representative) of the set to which a given element belongs. Path compression is applied to flatten the tree.

Union Operation (unionSets):

  • It performs the union of two sets (represented by their roots). Rank optimization is applied to keep the tree balanced.
  • If the roots are the same, adding an edge between the corresponding vertices would create a cycle.

4. Cycle Detection Function (isCyclic):

  • The isCyclic function initializes the DSU and iterates through the edges of the graph.
  • For each edge, it uses the DSU to check whether adding the edge would create a cycle.
  • If a cycle is detected, the function returns true; otherwise, it returns false.

5. Main Function:

  • In the main function, a sample graph is created with four vertices, and edges are added to create a cycle (0 -> 1 -> 2 -> 3 -> 0).
  • The isCyclic function is called, and the program prints whether the graph contains a cycle or not.

6. Output:

  • If the graph contains a cycle, the output will indicate that the graph has a cycle. In this example, it will print "The graph contains a cycle."
  • This approach efficiently detects cycles in undirected graphs using the Union-Find (DSU) data structure with path compression and rank optimization.

Complexity Analysis:

Time Complexity:

DSU Find and Union Operations:

The time complexity of the find operation with path compression is approximately O(α(V)), where α(V) is the inverse Ackermann function. This function grows extremely slowly and is considered practically constant for any realistic input size.

The time complexity of the unionSets operation with rank optimization is O(1) on average. This is because the rank ensures that the height of the tree remains relatively small.

Edge Iteration (isCyclic Function):

The loop in the isCyclic function iterates through each edge once, resulting in a time complexity of O(E), where E is the number of edges.

Overall Time Complexity:

The overall time complexity is dominated by the DSU operations and the edge iteration.

In practice, the overall time complexity is approximately O(V + E), where V is the number of vertices and E is the number of edges.

Space Complexity:

Graph Representation:

The space complexity for the graph is O(E), where E is the number of edges, due to the edge list representation. Each edge is represented by a pair of vertices.

DSU Data Structure:

The DSU data structure uses O(V) space to store the parent and rank arrays. Each vertex in the graph corresponds to one element in the DSU.

Additional Variables:

Other additional variables used in the functions, such as loop variables and function parameters, have negligible space requirements in the overall analysis.

Overall Space Complexity:

The overall space complexity is approximately O(V + E), where V is the number of vertices and E is the number of edges.

The primary space usage comes from the graph and the DSU data structure.

The provided algorithm is practical for detecting cycles in undirected graphs, offering a good balance between time and space efficiency. The Union-Find operations with path compression and rank optimization contribute to the overall effectiveness of the algorithm.

4. Topological Sort (for Directed Acyclic Graphs - DAGs):

Topological sorting is a systematic arrangement of vertices in a directed acyclic graph (DAG), ensuring that for every directed edge (u, v), vertex you precedes vertex v in the ordering. This ordering is exclusive to DAGs, where cyclic dependencies are absent, making it a valuable tool for modeling and managing task dependencies.

Each vertex in the topological order represents a task, and the order itself captures the sequential flow of tasks in a way that respects their interdependencies.

By adhering to this ordering, one can systematically organize and execute tasks in a manner that avoids circular dependencies. Topological sorting finds practical applications in project scheduling, task optimization, and job sequencing, providing a structured approach to managing complex systems with interrelated tasks and dependencies.

The Algorithm for topological sorting involves two main steps:

DFS-based Approach:

Perform a Depth-First Search (DFS) traversal on the graph.

Keep track of the finishing times of the vertices.

Use a stack to store vertices based on their finishing times.

Retrieve Topological Order:

Pop elements from the stack to get the topological order.

For detecting cycles in a directed graph, including Directed Acyclic Graphs (DAGs), one can employ Depth-First Search (DFS) and check for the presence of a back edge during the traversal. A back edge is an edge that connects a vertex to one of its ancestors in the DFS tree. If, during the DFS traversal, such a back edge is encountered, it signifies the existence of a cycle in the graph.

This approach leverages the nature of directed edges to identify cycles, as the presence of a back edge implies that there is a path from the ancestor vertex to the current vertex, forming a cycle in the directed graph. By carefully tracking the edges during DFS, this method effectively identifies cycles in both directed graphs and Directed Acyclic Graphs.

Below is a C++ implementation of topological sorting with cycle detection:

Output:

Topological Ordering: 0 1 2 3 4 5 

Explanation:

Let's delve into the details of the provided C++ code implementing Kahn's Algorithm for topological sorting and cycle detection.

1. Graph Class:

  • The Graph class represents a directed graph using an adjacency list (adjust).
  • The class includes a constructor to initialize the number of vertices (vertices) and a method (addEdge) to add directed edges to the graph.

2. Kahn's Algorithm Function (KahnTopologicalSort):

  • This function takes a Graph object as input and returns a vector representing the topological ordering of vertices or an empty vector if a cycle is detected.
  • It initializes vectors for in-degrees (inDegree), the result of topological sorting (result), and a queue (q) for processing nodes.
  • The in-degrees of each vertex are calculated by traversing the adjacency list. Nodes with in-degree 0 are enqueued to start the process.

3. Process Nodes:

  • The main loop dequeues a node, processes it (appends it to the result), and updates the in-degrees of its neighbors.
  • If the in-degree of a neighbor becomes 0, that neighbor is enqueued for processing in the next iteration.
  • This process continues until the queue is empty.

4. Checking for Cycles:

  • After processing all nodes, the function checks if all nodes are present in the result vector.
  • If they are, the graph is acyclic, and the result vector represents the topological ordering.
  • If not, the graph contains a cycle, and an empty vector is returned.

5. Main Function:

  • The main function creates a sample directed acyclic graph using the Graph class.
  • It adds directed edges to the graph.
  • The KahnTopologicalSort function is called to perform topological sorting.
  • Depending on the result, the program prints the topological ordering or indicates the presence of a cycle.

6. Output Example:

  • If the graph contains a cycle, the output will state that the graph contains a cycle.
  • If the graph is acyclic, it will print the topological ordering.

This implementation of Kahn's Algorithm is a concise and effective way to perform topological sorting and detect cycles in a directed acyclic graph. The use of in-degrees simplifies the identification of nodes with no incoming edges, facilitating a linear-time algorithm.

Complexity Analysis:

let's analyze the time and space complexity of the provided C++ code implementing Kahn's Algorithm for topological sorting and cycle detection:

Time Complexity:

Calculating In-Degrees:

The in-degrees of each vertex are calculated by traversing the adjacency list. This takes O(V + E) time, where V is the number of vertices, and E is the number of edges.

Main Loop (Processing Nodes):

In each iteration, a node is dequeued and the in-degrees of its neighbors are updated. This involves traversing the adjacency list.

The total time spent in the main loop is proportional to the number of edges and vertices, i.e., O(V + E).

Checking for Cycles:

The final check to see if all nodes are processed takes O(V) time.

Overall Time Complexity:

O(V + E) - The dominant factor is the traversal of both vertices and edges.

Space Complexity:

In-Degree Array (inDegree):

Requires O(V) space to store in-degrees for each vertex.

Result Vector (result):

Requires O(V) space to store the topological ordering.

Queue (q):

Requires O(V) space in the worst case when all nodes have in-degree 0.

Graph Representation (Adjacency List):

Requires O(V + E) space to represent the graph using an adjacency list.

Overall Space Complexity:

O(V + E) - The dominant factors are the in-degree array, result vector, and the graph representation.

Kahn's Algorithm is an efficient algorithm for topological sorting and cycle detection in directed acyclic graphs. The time and space complexity are both reasonable, making it suitable for practical use in various applications.

Conclusion:

  • Depth-First Search (DFS) explores graph nodes deeply, making it valuable for tasks like pathfinding and cycle detection. It traverses as far as possible along each branch before backtracking. Breadth-First Search (BFS), on the other hand, traverses levels, aiding in discovering the shortest path.
  • Union-Find (Disjoint Set Union - DSU) is an efficient tool for cycle detection and connecting components in a graph, crucial for maintaining connectivity information. Topological Sort orders directed acyclic graphs, providing valuable insights for task scheduling and dependency resolution. Each of these algorithms caters to specific graph problems, offering versatile tools for tasks such as pathfinding, connectivity analysis, and optimization in various applications.






Youtube For Videos Join Our Youtube Channel: Join Now

Feedback


Help Others, Please Share

facebook twitter pinterest

Learn Latest Tutorials


Preparation


Trending Technologies


B.Tech / MCA