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:
# 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)
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
#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
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
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
Output
[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.

