Detect cycle in an undirected graph - Summary


Detect cycle in an undirected graph | GeeksforGeeks
X. DFS: time O(V+E)
The time complexity of the union-find algorithm is O(ELogV). Like directed graphs, we can use DFS to detect cycle in an undirected graph in O(V+E) time. We do a DFS traversal of the given graph.

For every visited vertex ‘v’, if there is an adjacent ‘u’ such that u is already visited and u is not parent of v, then there is a cycle in graph. If we don’t find such an adjacent for any vertex, we say that there is no cycle.

If v is visited, and not current node's parent node(this is an undirected graph) ==> cycle found.
Difference from: Detect Cycle in a Directed Graph
http://massivealgorithms.blogspot.com/2014/07/detect-cycle-in-directed-graph.html
https://algorithms.tutorialhorizon.com/graph-detect-cycle-in-undirected-graph-using-dfs/
  • Do DFS from every vertex. (please read DFS here).
  • During DFS, for any current vertex ‘x’ (currently visiting vertex) if there an adjacent vertex ‘y’ is present which is already visited and no ‘y’ is not a direct parent of ‘x’ then there is a cycle in graph.
  • Why not parent:
    • Let’s assume, vertex ‘x’ and ‘y’ and we have edge between them. x—-y.
    • Now do DFS from ‘x’, once you reach to ‘y’, will do the DFS from ‘y’ and adjacent vertex is ‘x’ and since its already visited so there should be cycle but actually there is no cycle since ‘x’ is a parent of ‘y’. That is why we will ignore visited vertex if it is parent of current vertex.
  static class Graph {
    int vertices;
    LinkedList<Integer>[] adjList;

    public Graph(int vertices) {
      this.vertices = vertices;
      adjList = new LinkedList[vertices];
      for (int i = 0; i < vertices; i++) {
        adjList[i] = new LinkedList<>();
      }
    }

    public void addEdge(int source, int destination) {
      // add forward edge
      adjList[source].addFirst(destination);

      // add reverse edge
      adjList[destination].addFirst(source);
    }

    public boolean isCycle() {
      boolean result = false;

      // visited array
      boolean[] visited = new boolean[vertices];
      // do DFS, from each vertex
      for (int i = 0; i < vertices; i++) {
        if (visited[i] == false) {
          if (isCycleUtil(i, visited, -1)) {
            return true;
          }
        }
      }
      return result;
    }

    boolean isCycleUtil(int currVertex, boolean[] visited, int parent) {

      // add this vertex to visited vertex
      visited[currVertex] = true;

      // visit neighbors except its direct parent
      for (int i = 0; i < adjList[currVertex].size(); i++) {
        int vertex = adjList[currVertex].get(i);
        // check the adjacent vertex from current vertex
        if (vertex != parent) {
          // if destination vertex is not its direct parent then
          if (visited[vertex]) {
            // if here means this destination vertex is already visited
            // means cycle has been detected
            return true;
          } else {
            // recursion from destination node
            if (isCycleUtil(vertex, visited, currVertex)) {
              return true;
            }
          }
        }
      }
      return false;
    }

  }
In directed graph, the back edge may point to parent node.

// A recursive function that uses visited[] and parent to detect
// cycle in subgraph reachable from vertex v.
bool Graph::isCyclicUtil(int v, bool visited[], int parent)
{
    // Mark the current node as visited
    visited[v] = true;
    // Recur for all the vertices adjacent to this vertex
    list<int>::iterator i;
    for (i = adj[v].begin(); i != adj[v].end(); ++i)
    {
        // If an adjacent is not visited, then recur for that adjacent
        if (!visited[*i])
        {
           if (isCyclicUtil(*i, visited, v))
              return true;
        }
        // If an adjacent is visited and not parent of current vertex,
        // then there is a cycle.
        else if (*i != parent)
           return true;
    }
    return false;
}
// Returns true if the graph contains a cycle, else false.
bool Graph::isCyclic()
{
    // Mark all the vertices as not visited and not part of recursion
    // stack
    bool *visited = new bool[V];
    for (int i = 0; i < V; i++)
        visited[i] = false;
    // Call the recursive helper function to detect cycle in different
    // DFS trees
    for (int u = 0; u < V; u++)
        if (!visited[u] && isCyclicUtil(u, visited, -1))
            return true;
    return false;
}

X. Union-Find Algorithm | Set 1 (Detect Cycle in a an Undirected Graph)
We have also discussed a union-find algorithm for cycle detection in undirected graphs. The time complexity of the union-find algorithm is O(ELogV).
Note that the implementation of union() and find() is naive and takes O(n) time in worst case. These methods can be improved to O(Logn) using Union by Rank or Height.
https://algorithms.tutorialhorizon.com/graph-find-cycle-in-undirected-graph-using-disjoint-set-union-find/
  • The makeset operation makes a new set by creating a new element with a parent pointer to itself.
  • Then process each edge of the graph and perform find and Union operations to make subsets using both vertices of the edge.
  • If find operation on both the vertices returns the same parent (means both vertices belongs to the same subset) then cycle is detected.
  static class Edge {
    int source;
    int destination;

    public Edge(int source, int destination) {
      this.source = source;
      this.destination = destination;
    }
  }

  static class Graph {
    int vertices;
    LinkedList<Edge>[] adjList;
    ArrayList<Edge> allEdges = new ArrayList<>();

    Graph(int vertices) {
      this.vertices = vertices;
      adjList = new LinkedList[vertices];
      for (int i = 0; i < vertices; i++) {
        adjList[i] = new LinkedList<>();
      }
    }

    public void addEgde(int source, int destination) {
      Edge edge = new Edge(source, destination);
      adjList[source].addFirst(edge);
      allEdges.add(edge); // add to total edges
    }

    public void makeSet(int[] parent) {
      // Make set- creating a new element with a parent pointer to itself.
      for (int i = 0; i < vertices; i++) {
        parent[i] = i;
      }
    }

    public int find(int[] parent, int vertex) {
      // chain of parent pointers from x upwards through the tree
      // until an element is reached whose parent is itself
      if (parent[vertex] != vertex)
        return find(parent, parent[vertex]);
      ;
      return vertex;
    }

    public void union(int[] parent, int x, int y) {
      int x_set_parent = find(parent, x);
      int y_set_parent = find(parent, y);
      // make x as parent of y
      parent[y_set_parent] = x_set_parent;
    }

    public boolean isCycle() {
      // create a parent []
      int[] parent = new int[vertices];

      // makeset
      makeSet(parent);

      // iterate through all the edges and keep making the sets
      for (int i = 0; i < allEdges.size(); i++) {
        Edge edge = allEdges.get(i);
        int x_set = find(parent, edge.source);
        int y_set = find(parent, edge.destination);

        // check if source vertex and destination vertex belongs to the same set
        // if in same set then cycle has been detected else combine them into one set
        if (x_set == y_set)
          return true;
        else
          union(parent, x_set, y_set);
      }
      // if here, means cycle was not found
      return false;
    }

  }
http://www.geeksforgeeks.org/union-find/
We can't use UF for directed graph.
class Graph
{
    int V, E;    // V-> no. of vertices & E->no.of edges
    Edge edge[]; // /collection of all edges
    class Edge
    {
        int src, dest;
    };
    // Creates a graph with V vertices and E edges
    Graph(int v,int e)
    {
        V = v;
        E = e;
        edge = new Edge[E];
        for (int i=0; i<e; ++i)
            edge[i] = new Edge();
    }
    // A utility function to find the subset of an element i
    int find(int parent[], int i)
    {
        if (parent[i] == -1)
            return i;
        return find(parent, parent[i]);
    }
    // A utility function to do union of two subsets
    void Union(int parent[], int x, int y)
    {
        int xset = find(parent, x);
        int yset = find(parent, y);
        parent[xset] = yset;
    }
    // The main function to check whether a given graph
    // contains cycle or not
    int isCycle( Graph graph)
    {
        // Allocate memory for creating V subsets
        int parent[] = new int[graph.V];
        // Initialize all subsets as single element sets
        for (int i=0; i<graph.V; ++i)
            parent[i]=-1;
        // Iterate through all edges of graph, find subset of both
        // vertices of every edge, if both subsets are same, then
        // there is cycle in graph.
        for (int i = 0; i < graph.E; ++i)
        {
            int x = graph.find(parent, graph.edge[i].src);
            int y = graph.find(parent, graph.edge[i].dest);
            if (x == y)
                return 1;
            graph.Union(parent, x, y);
        }
        return 0;
    }
}
Note that the implementation of union() and find() is naive and takes O(n) time in worst case. These methods can be improved to O(Logn) using Union by Rank or Height.

BFS
Why DFS and not BFS for finding cycle in graphs
Depth first search is more memory efficient than breadth first search as you can backtrack sooner.
Once DFS finds a cycle, the stack will contain the nodes forming the cycle. The same is not true for BFS, so you need to do extra work if you want to also print the found cycle.
Also if your graph is directed then you have to not just remember if you have visited a node or not, but also how you got there.

http://stackoverflow.com/questions/4464336/pseudocode-to-find-cycles-in-a-graph-using-breadth-first-search
n an undirected graph, it's a traditional BFS that aborts and reports a cycle found when it reaches a node previously marked as visited. You can find pseudocode for BFS here.


In a directed graph, it gets trickier, since you have to remember which way were you walking when you reached the node, and the spatial complexity disadvantage over DFS gets even worse.

bool isCyclicConntected(vector<int> adj[], int s,
                        int V, vector<bool>& visited)
{
    // Set parent vertex for every vertex as -1.
    vector<int> parent(V, -1);
  
    // Create a queue for BFS
    queue<int> q;
  
    // Mark the current node as visited and enqueue it
    visited[s] = true;
    q.push(s);
  
    while (!q.empty()) {
  
        // Dequeue a vertex from queue and print it
        int u = q.front();
        q.pop();
  
        // Get all adjacent vertices of the dequeued
        // vertex s. If a adjacent has not been visited,
        // then mark it visited and enqueue it. We also
        // mark parent so that parent is not considered
        // for cycle.
        for (auto v : adj[u]) {
            if (!visited[v]) {
                visited[v] = true;
                q.push(v);
                parent[v] = u;
            }
            else if (parent[u] != v)
                return true;
        }
    }
    return false;
}
  
bool isCyclicDisconntected(vector<int> adj[], int V)
{
    // Mark all the vertices as not visited
    vector<bool> visited(V, false);
  
    for (int i = 0; i < V; i++)
        if (!visited[i] && isCyclicConntected(adj, i,
                                         V, visited))
            return true;
    return false;
}

Each edge is considered at most twice (once for each of its end vertices), and since we go over every vertex of the graph, the overall complexity is O(V+E).
def is_cyclic_graph(G):
    Q = []
    V = G.vertices()
    # initially all vertices are unexplored
    layer = { v: -1 for v in V }
    for v in V:
        # v has already been explored; move on
        if layer[v] != -1:
            continue
        # take v as a starting vertex
        layer[v] = 0
        Q.append(v)
        # as long as Q is not empty
        while len(Q) > 0:
            # get the next vertex u of Q that must be looked at
            u = Q.pop(0)
            C = G.vertex_neighbors(u)
            for z in C:
                # if z is being found for the first time
                if layer[z] == -1:
                    layer[z] = layer[u] + 1
                    Q.append(z)
                elif layer[z] >= layer[u]:
                    return True
    return False

X. Detect Cycle in a Directed Graph using colors
https://algorithms.tutorialhorizon.com/graph-detect-cycle-in-a-directed-graph-using-colors/
https://www.geeksforgeeks.org/detect-cycle-direct-graph-using-colors/
WHITE : Vertex is not processed yet.  Initially
        all vertices are WHITE.

GRAY : Vertex is being processed (DFS for this 
       vertex has started, but not finished which means
       that all descendants (ind DFS tree) of this vertex
       are not processed yet (or this vertex is in function
       call stack)

BLACK : Vertex and all its descendants are 
        processed.

While doing DFS, if we encounter an edge from current 
vertex to a GRAY vertex, then this edge is back edge 
and hence there is a cycle.

// Recursive function to find if there is back edge
// in DFS subtree tree rooted with 'u'
bool Graph::DFSUtil(int u, int color[])
{
    // GRAY :  This vertex is being processed (DFS
    //         for this vertex has started, but not
    //         ended (or this vertex is in function
    //         call stack)
    color[u] = GRAY;
  
    // Iterate through all adjacent vertices
    list<int>::iterator i;
    for (i = adj[u].begin(); i != adj[u].end(); ++i)
    {
        int v = *i;  // An adjacent of u
  
        // If there is
        if (color[v] == GRAY)
          return true;
  
        // If v is not processed and there is a back
        // edge in subtree rooted with v
        if (color[v] == WHITE && DFSUtil(v, color))
          return true;
    }
  
    // Mark this vertex as processed
    color[u] = BLACK;
  
    return false;
}
  
// Returns true if there is a cycle in graph
bool Graph::isCyclic()
{
    // Initialize color of all vertices as WHITE
    int *color = new int[V];
    for (int i = 0; i < V; i++)
        color[i] = WHITE;
  
    // Do a DFS traversal beginning with all
    // vertices
    for (int i = 0; i < V; i++)
        if (color[i] == WHITE)
           if (DFSUtil(i, color) == true)
              return true;
  
    return false;
}

Read full article from Detect cycle in an undirected graph | GeeksforGeeks

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) å°ŗå–ę³• (11) ꌑꈘē؋åŗč®¾č®”ē«žčµ› (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) ē©·ē«­ęœē“¢ (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) č“ę”„ęÆ (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts