Check if a cycle exists between nodes S and T in an Undirected Graph with only S and T repeating | Set – 2
Given an undirected graph with N nodes and two vertices S & T, the task is to check if a cycle between these two vertices exists (and return it) or not, such that no other node except S and T appears more than once in that cycle.
Examples:
Input: N = 7, edges[][] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 2}, {2, 6}, {6, 0}}, S = 0, T = 4
Output: No simple cycle from S to T exists
Explanation: No simple cycle from S to T exists,
because node 2 appears two times, in the only cycle that exists between 0 & 4Input: N = 7, edges[][] = {{0, 1}, {1, 2}, {1, 6}, {2, 3}, {3, 4}, {4, 5}, {5, 2}, {2, 6}, {6, 0}},, S = 0, T = 4
Output: 0->1->3->4->5->2->6->0
Explanation: The cycle doesn’t repeat any node (except 0)
Naive approach: The naive approach of the problem is discussed in Set-1 of this problem.
Efficient Approach: In the naive approach there is checking for all possible paths. The idea in this approach is similar to the Ford Fulkerson algorithm with Edmonds-Karp implementation, but with only 2 BFS. Follow the below steps to solve the problem
- First, make a directed graph by duplicating each node (except S and T) in receiver and sender:
- If the original graph had the edge: {a, b}, the new graph will have {sender_a, receiver_b}
- The only node that points to a sender is his receiver, so the only edge that ends in sender_v is: {receiver_v, sender_v}
- The receiver only points to his sender, so the adjacency list of receiver_v is: [sender_v]
- Run a BFS to find a path from S to T, and memorize the path back (using a predecessor array).
- Invert the edges in the path found, this step is similar to the update of an augmenting path in Ford Fulkerson.
- While inverting memorize the flow from one node to another in the path
- So, if the previous node of cur is pred, then flow[cur] = pred
- Finally, memorize the last node (before the node t), let’s call it: first_node (because it’s the first node, after t, of the flow_path), first_node = flow[t]
- Run a BFS again to find the second path, and memorize the path back (using a predecessor array).
- Memorize the flow of the second path again:
- Only mark the flow if there wasn’t a previous flow in an opposite direction, this way two opposite flows will be discarded. Therefore, if flow[pred] == cur don’t do: flow[cur] = pred
- If the previous node of cur is pred in a path, then flow[cur] = pred
- Finally, join the paths:
- Traverse both paths by the flow, one path starting in first_node and the other flow[t]
- As we have 2 paths from t to s, by reverting one of them we will have one path from s to t and another from t to s.
- Traverse one path from s to t, and the other from t to s.
All this work duplicating the graph and registering the flow is done to assure that the same node won’t be traversed twice.
Below is the implementation of the above approach:
Python3
# Python program for the above approach # Auxiliary data struct for the BFS: class Node: def __init__( self , val): self .val = val self . next = None class queue: def __init__( self ): self .head = None self .tail = None def empty( self ): return self .head = = None def push( self , val): if self .head is None : self .head = Node(val) self .tail = self .head else : self .tail. next = Node(val) self .tail = self .tail. next def pop( self ): returned = self .head.val self .head = self .head. next return returned # BFS to find the paths def bfs(graph, s, t): # Number of nodes in original graph N = len (graph) / / 2 Q = queue() Q.push(s) predecessor = list ( - 1 for _ in range ( 2 * N)) predecessor[s] = s while not Q.empty(): cur = Q.pop() # Add neighbors to the queue for neighbour in graph[cur]: # If we reach node we found the path if neighbour = = t or neighbour = = t + N: predecessor[t] = cur predecessor[t + N] = cur return predecessor # Not seen if predecessor[neighbour] = = - 1 : Q.push(neighbour) predecessor[neighbour] = cur return None # Invert the path and register flow def invert_path(graph, predecessor, flow, s, t): N = len (graph) / / 2 cur = t while cur ! = s: pred = predecessor[cur] if flow[pred] ! = cur: flow[cur] = pred # Reverse edge graph[cur].append(pred) graph[pred].remove(cur) cur = pred # Node S and T are not duplicated # so we don't reverse the edge s->(s + N) # because it shouldn't exist graph[s].append(s + N) return flow # Return the path by the flow def flow_path(flow, first_node, s): path = [] cur = first_node while cur ! = s: path.append(cur) cur = flow[cur] return path # Function to get the cyclle with 2 nodes def cycleWith2Nodes(graph, s = 0 , t = 1 ): # Number of nodes in the graph N = len (graph) # Duplicate nodes: # Adjacency list of sender nodes graph + = list (graph[node] for node in range (N)) # Adjacency list of receiver nodes graph[:N] = list ([node + N] for node in range (N)) # print('duplicated graph:', graph, '\n') # Find a path from s to t predecessor = bfs(graph, s, t) if predecessor is not None : # List to memorize the flow # flow from node v is: # flow[v], which gives the node who # receives the flow flow = list ( - 1 for _ in range ( 2 * N)) flow = invert_path(graph, predecessor, flow, s, t) first_node = flow[t] else : print ( "No cycle" ) return # Find second path predecessor = bfs(graph, s, t) if predecessor is not None : flow = invert_path(graph, predecessor, flow, s, t) # Combine both paths: # From T to S path1 = flow_path(flow, first_node, s) path2 = flow_path(flow, flow[t], s) # Reverse the second path # so we will have another path # but from s to t path2.reverse() simpleCycle = [s] + path2 + [t] + path1 + [s] print (simpleCycle[:: 2 ]) else : print ( "No cycle" ) # Driver Code if __name__ = = "__main__" : graph = [ [ 1 , 6 ], # 0 [ 0 , 2 , 3 ], # 1 [ 1 , 3 , 5 , 6 ], # 2 [ 1 , 2 , 4 ], # 3 [ 3 , 5 ], # 4 [ 2 , 4 ], # 5 [ 0 , 2 ], # 6 ] cycleWith2Nodes(graph, s = 0 , t = 4 ) |
Java
import java.util.*; public class CycleBetweenVertices { static int N; static List<Integer>[] graph; static int [] parent; static boolean [] visited; static boolean foundCycle; static List<Integer> cycle; static int S, T; public static List<Integer> findCycle( int n, int [][] edges, int s, int t) { N = n; graph = new List[N]; parent = new int [N]; visited = new boolean [N]; foundCycle = false ; cycle = new ArrayList<>(); S = s; T = t; for ( int i = 0 ; i < N; i++) { graph[i] = new ArrayList<>(); parent[i] = - 1 ; } for ( int [] edge : edges) { int u = edge[ 0 ]; int v = edge[ 1 ]; graph[u].add(v); graph[v].add(u); } dfs(S, - 1 ); if (!foundCycle) { return new ArrayList<>(); } Collections.reverse(cycle); return cycle; } public static boolean dfs( int u, int p) { visited[u] = true ; parent[u] = p; for ( int v : graph[u]) { if (foundCycle) { return true ; } if (!visited[v]) { if (dfs(v, u)) { return true ; } } else if (v != p && (v == S || v == T)) { foundCycle = true ; cycle.add(v); for ( int i = u; i != v; i = parent[i]) { cycle.add(i); } cycle.add(v); return true ; } } return false ; } public static void main(String[] args) { int n = 7 ; int [][] edges = {{ 0 , 1 }, { 1 , 2 }, { 1 , 6 }, { 2 , 3 }, { 3 , 4 }, { 4 , 5 }, { 5 , 2 }, { 2 , 6 }, { 6 , 0 }}; int s = 0 ; int t = 4 ; List<Integer> cycle = findCycle(n, edges, s, t); if (cycle.isEmpty()) { System.out.println( "No cycle exists between " + s + " and " + t + "." ); } else { System.out.print( "Cycle between " + s + " and " + t + ": " ); for ( int i = 0 ; i < cycle.size(); i++) { System.out.print(cycle.get(i)); if (i < cycle.size() - 1 ) { System.out.print( " -> " ); } } System.out.println(); } } } //This code is contributed by Akash Jha |
C++
#include <bits/stdc++.h> using namespace std; int N; vector< int > graph[10001]; int parent[10001]; bool visited[10001]; bool foundCycle; vector< int > cycle; int S, T; bool dfs( int u, int p); vector< int > findCycle( int n, vector<vector< int >>& edges, int s, int t) { N = n; foundCycle = false ; cycle.clear(); S = s; T = t; for ( int i = 0; i < N; i++) { graph[i].clear(); parent[i] = -1; visited[i] = false ; } for ( auto edge : edges) { int u = edge[0]; int v = edge[1]; graph[u].push_back(v); graph[v].push_back(u); } dfs(S, -1); if (!foundCycle) { return vector< int >(); } reverse(cycle.begin(), cycle.end()); return cycle; } bool dfs( int u, int p) { visited[u] = true ; parent[u] = p; for ( auto v : graph[u]) { if (foundCycle) { return true ; } if (!visited[v]) { if (dfs(v, u)) { return true ; } } else if (v != p && (v == S || v == T)) { foundCycle = true ; cycle.push_back(v); for ( int i = u; i != v; i = parent[i]) { cycle.push_back(i); } cycle.push_back(v); return true ; } } return false ; } int main() { int n = 7; vector<vector< int >> edges = {{0, 1}, {1, 2}, {1, 6}, {2, 3}, {3, 4}, {4, 5}, {5, 2}, {2, 6}, {6, 0}}; int s = 0; int t = 4; vector< int > cycle = findCycle(n, edges, s, t); if (cycle.empty()) { cout << "No cycle exists between " << s << " and " << t << "." << endl; } else { cout << "Cycle between " << s << " and " << t << ": " ; for ( int i = 0; i < cycle.size(); i++) { cout << cycle[i]; if (i < cycle.size() - 1) { cout << " -> " ; } } cout << endl; } return 0; } //This code is contributed by Akash Jha |
C#
using System; using System.Collections.Generic; using System.Linq; class Program { static int N; static List< int >[] graph; static int [] parent; static bool [] visited; static bool foundCycle; static List< int > cycle; static int S, T; static bool dfs( int u, int p) { visited[u] = true ; parent[u] = p; foreach ( int v in graph[u]) { if (foundCycle) { return true ; } if (!visited[v]) { if (dfs(v, u)) { return true ; } } else if (v != p && (v == S || v == T)) { foundCycle = true ; cycle.Add(v); for ( int i = u; i != v; i = parent[i]) { cycle.Add(i); } cycle.Add(v); return true ; } } return false ; } static List< int > FindCycle( int n, int [][] edges, int s, int t) { N = n; foundCycle = false ; cycle = new List< int >(); S = s; T = t; graph = new List< int >[N]; parent = new int [N]; visited = new bool [N]; for ( int i = 0; i < N; i++) { graph[i] = new List< int >(); parent[i] = -1; visited[i] = false ; } foreach ( var edge in edges) { int u = edge[0]; int v = edge[1]; graph[u].Add(v); graph[v].Add(u); } dfs(S, -1); if (!foundCycle) { return new List< int >(); } cycle.Reverse(); return cycle; } static void Main( string [] args) { int n = 7; int [][] edges = new int [][] { new int [] {0, 1}, new int [] {1, 2}, new int [] {1, 6}, new int [] {2, 3}, new int [] {3, 4}, new int [] {4, 5}, new int [] {5, 2}, new int [] {2, 6}, new int [] {6, 0}, }; int s = 0; int t = 4; List< int > cycle = FindCycle(n, edges, s, t); if (cycle.Count == 0) { Console.WriteLine($ "No cycle exists between {s} and {t}." ); } else { Console.Write($ "Cycle between {s} and {t}: " ); Console.WriteLine( string .Join( " -> " , cycle)); } } } //This code is contributed by AKash Jha |
Javascript
let N; let graph = new Array(10001).fill().map(() => []); let parent = new Array(10001).fill(-1); let visited = new Array(10001).fill( false ); let foundCycle; let cycle; let S, T; function findCycle(n, edges, s, t) { N = n; foundCycle = false ; cycle = []; S = s; T = t; for (let i = 0; i < N; i++) { graph[i].length = 0; parent[i] = -1; visited[i] = false ; } for (let edge of edges) { let u = edge[0]; let v = edge[1]; graph[u].push(v); graph[v].push(u); } dfs(S, -1); if (!foundCycle) { return []; } cycle.reverse(); return cycle; } function dfs(u, p) { visited[u] = true ; parent[u] = p; for (let v of graph[u]) { if (foundCycle) { return true ; } if (!visited[v]) { if (dfs(v, u)) { return true ; } } else if (v !== p && (v === S || v === T)) { foundCycle = true ; cycle.push(v); for (let i = u; i !== v; i = parent[i]) { cycle.push(i); } cycle.push(v); return true ; } } return false ; } let n = 7; let edges = [[0, 1], [1, 2], [1, 6], [2, 3], [3, 4], [4, 5], [5, 2], [2, 6], [6, 0]]; let s = 0; let t = 4; let cycleResult = findCycle(n, edges, s, t); if (cycleResult.length === 0) { console.log( "No cycle exists between " + s + " and " + t + "." ); } else { console.log( "Cycle between " + s + " and " + t + ": " ); for (let i = 0; i < cycleResult.length; i++) { console.log(cycleResult[i]); if (i < cycleResult.length - 1) { console.log( " -> " ); } } console.log(); } //This code is contributed by Akash Jha |
[0, 6, 2, 5, 4, 3, 1, 0]
Complexity Analysis: Given below are the complexity for each function
- Create duplicate graph: O(V+E)
- 2 BFS: O(V+E)
- 2 inversions (registering flow) : O(path size) <= O(V+E)
- Recreate the paths from the flow array: O(path size) <= O(V+E)
- Reverse one path: O(path size) <= O(V+E)
Time Complexity: O(V+E)
Auxiliary Space: O(N*N), where N is the count of vertices in the graph.
Please Login to comment...