Open In App

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

Improve
Improve
Like Article
Like
Save
Share
Report

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}
    • The receiver only points to his sender, so the adjacency list of receiver_v is: [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)


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


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.



Last Updated : 04 Apr, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads