# Check if a cycle exists between nodes S and T in an Undirected Graph with only S and T repeating | Set – 2

• Last Updated : 30 Mar, 2022

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 & 4

Input: 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

1. 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}
2. Run a BFS to find a path from S to T, and memorize the path back (using a predecessor array).
3. 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]
4. Run a BFS again to find the second path, and memorize the path back (using a predecessor array).
5. 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
6. 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``)`

Output

`[0, 6, 2, 5, 4, 3, 1, 0]`

Complexity Analysis: Given below are the complexity for each function

1. Create duplicate graph: O(V+E)
2. 2 BFS:  O(V+E)
3. 2 inversions (registering flow) : O(path size) <= O(V+E)
4. Recreate the paths from the flow array: O(path size) <= O(V+E)
5. 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.

My Personal Notes arrow_drop_up
Recommended Articles
Page :