Check if a cycle between nodes S and T in an Undirected Graph with only S and T repeating


Introduction

Graphs are powerful mathematical structures that allow us to model and visualize relationships between various entities. In computer science, graphs find application in a wide range of algorithms and data structures. One common problem with undirected graphs is determining whether a cycle exists between two given nodes. In this article, we embark upon the journey to unravel this mystery and present an elegant solution using C/C++. Determining cycles within an undirected graph is vital in various applications where connectivity matters.

Undirected Graphs is determining whether a cycle exists between two given nodes

Unweighted bidirectional (or undirected) graphs consist of vertices or nodes connected by edges. These edges hold no specific weight or distance assigned to them but instead only indicate a connection exists without any directional bias.

A path represents a sequence of interconnected vertices where each vertex is directly linked to its adjacent counterparts through edges. Here, we focus on finding paths that connect two distinct vertices explicitly called source and destination nodes with minimal total edge traversals.

Consider an undirected graph where each edge represents a connection between two nodes. Our goal is to determine if there is a cycle connecting node S and node T within this graph when both nodes can repeat themselves along any path.

In the above graph, it has four vertices and 3 edges and we need to check whether there is a cycle between node 1 and node 4. The DFS algorithm starts at node 1 (source) and visits its adjacent nodes - nodes 2 and 3. It then visits node 4 which has already been visited before by node 3. Since node 4 is not the parent of node 3, there is a cycle in the graph.

Approach 1: C++ Program to check if a cycle between node S and T in an Undirected graph with only S and T repeating

To solve this problem efficiently, we will employ Depth-First Search (DFS), a widely-used graph traversal algorithm. The basic idea behind DFS is to explore as far as possible down each branch before backtracking.

Algorithm

  • Step 1 − It uses a `Graph` class that encapsulates the graph's properties.

  • Step 2 − It includes functions like `addEdge()` to connect two vertices, `hasCycle()` to determine if there is a cycle between two nodes and internal helper methods such as `dfsUtil()` for DFS traversal.

  • Step 3 − In the driver code (`main()`), we prompt the user to input the number of vertices and edges in our graph.

  • Step 4 − Then ask for edge pairs (node connections) one by one.

  • Step 5 − Finally, ask for a source and target vertex.

  • Step 6 − The program will output whether there exists a path between these two nodes with possible repetitions or not.

Example

#include <iostream>
#include <vector>

using namespace std;

// Graph class representing our underlying structure
class Graph {
   int numNodes; // Total number of nodes in the graph
   vector<vector<int>> adjList; // Adjacency list representation
    
   public:
      // Constructor taking input parameter specifying the total number of nodes
      Graph(int n) {
         numNodes = n;
         adjList.resize(n);
      }
    
      // Function allowing adding edges between two vertices u and v (0-based indexing)
      void addEdge(int u, int v) {
         adjList[u].push_back(v);
         adjList[v].push_back(u);
      }
    
   private:
      // Utility function used by hasCycle() to perform DFS search recursively starting from vertex s.
      // It returns true if the cycle is found during exploration.
      bool dfsUtil(int s, bool visited[], bool parent[]) {
         visited[s] = true;
    
         for (int v : adjList[s]) {
            if (!visited[v]) {
               parent[v] = true;
            
               // Recursive call
               if (dfsUtil(v, visited, parent))
                  return true;
            }
            else if(parent[v])
               return true;
         }
        
         return false;   
      }
    
   public:
      // Function that checks whether a cycle exists between nodes S and T.
      bool hasCycle(int s, int t) {
         bool* visited = new bool[numNodes];
         bool* parent = new bool[numNodes];
        
         for(int i=0; i<numNodes; ++i){
            visited[i]=false;
            parent[i]=false;
         }
         parent[s] = true;

         if(dfsUtil(s, visited, parent)){
            delete[] visited;
            delete[] parent;

            return dfsUtil(t,visited,parent);
         }
     
         delete[] visited;

         return false; 
      }

};
int main() {
   int numVertices = 4;
   int numEdges = 4;

   Graph graph(numVertices);

   vector<pair<int,int>> edges = {{1,2},{2,3},{3,4},{4,1}};

   for(auto edge : edges){
      int u = edge.first - 1;
      int v = edge.second - 1;
      graph.addEdge(u,v);
   }

   int source = 1 - 1;
   int target = 4 - 1;

   if(graph.hasCycle(source,target))
      cout<<"A cycle exists between node "<< source+1 <<" and node " <<target+1<<".";
   else
      cout<<"No cycle found between node "<< source+1<<" and node " <<target + 1<<".";

   return 0;
}

Output

A cycle exists between node 1 and node 4.

Conclusion

This problem becomes even more interesting when cycles can be formed by repeating specific nodes along different paths. By utilizing Depth-First Search (DFS), we have successfully developed robust C++ code that incorporates solutions to explore graphs while efficiently checking for the existence of cycles connecting repeats of selected nodes S and T.

Updated on: 25-Aug-2023

39 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements