Union-Find Algorithm | Set 2 (Union By Rank and Path Compression)

17 Jun, 2022
In the previous post, we introduced union find algorithm and used it to detect cycle in a graph. We used following union() and find() operations for subsets.
C++
Java
Python3
C#
Javascript
// Naive implementation of findint find(int parent[], int i){    if (parent[i] == -1)        return i;    return find(parent, parent[i]);}  // Naive implementation of union()void Union(int parent[], int x, int y){    int xset = find(parent, x);    int yset = find(parent, y);    parent[xset] = yset;}
// Naive implementation of findstatic int find(int parent[], int i){    if (parent[i] == -1)        return i;    return find(parent, parent[i]);}   // Naive implementation of union()static void Union(int parent[], int x, int y){    int xset = find(parent, x);    int yset = find(parent, y);    parent[xset] = yset;} // This code is contributed by divyesh072019
# Naive implementation of finddef find(parent, i):         if (parent[i] == -1):        return i         return find(parent, parent[i]) # Naive implementation of union()def Union(parent, x, y):     xset = find(parent, x)    yset = find(parent, y)    parent[xset] = yset # This code is contributed by rutvik_56
// Naive implementation of findstatic int find(int []parent, int i){    if (parent[i] == -1)        return i;             return find(parent, parent[i]);}   // Naive implementation of union()static void Union(int []parent, int x, int y){    int xset = find(parent, x);    int yset = find(parent, y);    parent[xset] = yset;} // This code is contributed by pratham76
<script> // Naive implementation of findfunction find(parent, i){    if (parent[i] == -1)        return i;             return find(parent, parent[i]);}   // Naive implementation of union()function Union(parent, x, y){    let xset = find(parent, x);    let yset = find(parent, y);    parent[xset] = yset;} <script>
The above union() and find() are naive and the worst case time complexity is linear. The trees created to represent subsets can be skewed and can become like a linked list. Following is an example worst case scenario. 
Let there be 4 elements 0, 1, 2, 3

Initially, all elements are single element subsets.
0 1 2 3 

Do Union(0, 1)
   1   2   3  
  /
 0

Do Union(1, 2)
     2   3   
    /
   1
 /
0

Do Union(2, 3)
         3    
        /
      2
     /
   1
 /
0
The above operations can be optimized to O(Log n) in worst case. The idea is to always attach smaller depth tree under the root of the deeper tree. This technique is called union by rank. The term rank is preferred instead of height because if path compression technique (we have discussed it below) is used, then rank is not always equal to height. Also, size (in place of height) of trees can also be used as rank. Using size as rank also yields worst case time complexity as O(Logn).
Let us see the above example with union by rank
Initially, all elements are single element subsets.
0 1 2 3 

Do Union(0, 1)
   1   2   3  
  /
 0

Do Union(1, 2)
   1    3
 /  \
0    2

Do Union(2, 3)
    1    
 /  |  \
0   2   3
The second optimization to naive method is Path Compression. The idea is to flatten the tree when find() is called. When find() is called for an element x, root of the tree is returned. The find() operation traverses up from x to find root. The idea of path compression is to make the found root as parent of x so that we don’t have to traverse all intermediate nodes again. If x is root of a subtree, then path (to root) from all nodes under x also compresses.
Let the subset {0, 1, .. 9} be represented as below and find() is called
for element 3.
             9
         /   |   \  
        4    5    6
       /         /  \
      0         7    8
     /        
    3
   / \         
  1   2
When find() is called for 3, we traverse up and find 9 as representative
of this subset. With path compression, we also make 3 and 0 as the child of 9 so 
that when find() is called next time for 0, 1, 2 or 3, the path to root is reduced.

        --------9-------
      /   /    /  \      \
     0   4    5    6       3 
                  /  \    /  \
                 7    8   1   2
The two techniques complement each other. The time complexity of each operation becomes even smaller than O(Logn). In fact, amortized time complexity effectively becomes small constant. 
Following is union by rank and path compression based implementation to find a cycle in a graph. 
C++
C
Java
Python3
C#
Javascript
// A C++ program to detect cycle in a graph using union by// rank and path compression#include <bits/stdc++.h>using namespace std; // a structure to represent an edge in the graphstruct Edge {    int src, dest;}; // a structure to represent a graphstruct Graph {    // V-> Number of vertices, E-> Number of edges    int V, E;     // graph is represented as an array of edges    struct Edge* edge;}; struct subset {    int parent;    int rank;}; // Creates a graph with V vertices and E edgesstruct Graph* createGraph(int V, int E){    struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));    graph->V = V;    graph->E = E;     graph->edge = (struct Edge*)malloc(graph->E * sizeof(struct Edge));     return graph;} // A utility function to find set of an element i// (uses path compression technique)int find(struct subset subsets[], int i){    // find root and make root as parent of i (path    // compression)    if (subsets[i].parent != i)        subsets[i].parent = find(subsets, subsets[i].parent);     return subsets[i].parent;} // A function that does union of two sets of x and y// (uses union by rank)void Union(struct subset subsets[], int xroot, int yroot){     // Attach smaller rank tree under root of high rank tree    // (Union by Rank)    if (subsets[xroot].rank < subsets[yroot].rank)        subsets[xroot].parent = yroot;    else if (subsets[xroot].rank > subsets[yroot].rank)        subsets[yroot].parent = xroot;     // If ranks are same, then make one as root and    // increment its rank by one    else {        subsets[yroot].parent = xroot;        subsets[xroot].rank++;    }} // The main function to check whether a given graph contains// cycle or notint isCycle(struct Graph* graph){    int V = graph->V;    int E = graph->E;     // Allocate memory for creating V sets    struct subset* subsets        = (struct subset*)malloc(V * sizeof(struct subset));     for (int v = 0; v < V; ++v) {        subsets[v].parent = v;        subsets[v].rank = 0;    }     // Iterate through all edges of graph, find sets of both    // vertices of every edge, if sets are same, then there    // is cycle in graph.    for (int e = 0; e < E; ++e) {        int x = find(subsets, graph->edge[e].src);        int y = find(subsets, graph->edge[e].dest);         if (x == y)            return 1;         Union(subsets, x, y);    }    return 0;} // Driver codeint main(){    /* Let us create the following graph         0        |  \        |    \        1-----2 */     int V = 3, E = 3;    struct Graph* graph = createGraph(V, E);     // add edge 0-1    graph->edge[0].src = 0;    graph->edge[0].dest = 1;     // add edge 1-2    graph->edge[1].src = 1;    graph->edge[1].dest = 2;     // add edge 0-2    graph->edge[2].src = 0;    graph->edge[2].dest = 2;     if (isCycle(graph))        cout << "Graph contains cycle";    else        cout << "Graph doesn't contain cycle";     return 0;} // This code is contributed by Aditya Kumar (adityakumar129)
// A C program to detect cycle in a graph using union by// rank and path compression#include <stdio.h>#include <stdlib.h> // a structure to represent an edge in the graphtypedef struct Edge {    int src, dest;}Edge; // a structure to represent a graphtypedef struct Graph {    // V-> Number of vertices, E-> Number of edges    int V, E;     // graph is represented as an array of edges    struct Edge* edge;}Graph; typedef struct subset {    int parent;    int rank;}subset; // Creates a graph with V vertices and E edgesGraph* createGraph(int V, int E){    Graph* graph = (Graph*)malloc(sizeof(Graph));    graph->V = V;    graph->E = E;    graph->edge = (Edge*)malloc(graph->E * sizeof(Edge));    return graph;} // A utility function to find set of an element i// (uses path compression technique)int find(subset subsets[], int i){    // find root and make root as parent of i (path    // compression)    if (subsets[i].parent != i)        subsets[i].parent = find(subsets, subsets[i].parent);    return subsets[i].parent;} // A function that does union of two sets of x and y// (uses union by rank)void Union(subset subsets[], int xroot, int yroot){    // Attach smaller rank tree under root of high rank tree    // (Union by Rank)    if (subsets[xroot].rank < subsets[yroot].rank)        subsets[xroot].parent = yroot;    else if (subsets[xroot].rank > subsets[yroot].rank)        subsets[yroot].parent = xroot;    // If ranks are same, then make one as root and    // increment its rank by one    else {        subsets[yroot].parent = xroot;        subsets[xroot].rank++;    }} // The main function to check whether a given graph contains// cycle or notint isCycle(Graph* graph){    int V = graph->V;    int E = graph->E;    // Allocate memory for creating V sets    subset* subsets = (subset*)malloc(V * sizeof(subset));    for (int v = 0; v < V; ++v) {        subsets[v].parent = v;        subsets[v].rank = 0;    }    // Iterate through all edges of graph, find sets of both    // vertices of every edge, if sets are same, then there    // is cycle in graph.    for (int e = 0; e < E; ++e) {        int x = find(subsets, graph->edge[e].src);        int y = find(subsets, graph->edge[e].dest);        if (x == y)            return 1;        Union(subsets, x, y);    }    return 0;} // Driver codeint main(){    /* Let us create the following graph         0        |  \        |    \        1-----2 */     int V = 3, E = 3;    Graph* graph = createGraph(V, E);     // add edge 0-1    graph->edge[0].src = 0;    graph->edge[0].dest = 1;     // add edge 1-2    graph->edge[1].src = 1;    graph->edge[1].dest = 2;     // add edge 0-2    graph->edge[2].src = 0;    graph->edge[2].dest = 2;     if (isCycle(graph))        printf("Graph contains cycle");    else        printf("Graph doesn't contain cycle");    return 0;} // This code is contributed by Aditya Kumar (adityakumar129)
// A union by rank and path compression// based program to detect cycle in a graphclass Graph{    int V, E;    Edge[] edge;     Graph(int nV, int nE)    {        V = nV;        E = nE;        edge = new Edge[E];        for (int i = 0; i < E; i++)        {            edge[i] = new Edge();        }    }     // class to represent edge    class Edge    {        int src, dest;    }     // class to represent Subset    class subset    {        int parent;        int rank;    }     // A utility function to find    // set of an element i (uses    // path compression technique)    int find(subset[] subsets, int i)    {        if (subsets[i].parent != i)            subsets[i].parent                = find(subsets, subsets[i].parent);        return subsets[i].parent;    }     // A function that does union    // of two sets of x and y    // (uses union by rank)    void Union(subset[] subsets, int x, int y)    {        int xroot = find(subsets, x);        int yroot = find(subsets, y);         if (subsets[xroot].rank < subsets[yroot].rank)            subsets[xroot].parent = yroot;        else if (subsets[yroot].rank < subsets[xroot].rank)            subsets[yroot].parent = xroot;        else {            subsets[xroot].parent = yroot;            subsets[yroot].rank++;        }    }     // The main function to check whether    // a given graph contains cycle or not    int isCycle(Graph graph)    {        int V = graph.V;        int E = graph.E;         subset[] subsets = new subset[V];        for (int v = 0; v < V; v++) {             subsets[v] = new subset();            subsets[v].parent = v;            subsets[v].rank = 0;        }         for (int e = 0; e < E; e++) {            int x = find(subsets, graph.edge[e].src);            int y = find(subsets, graph.edge[e].dest);            if (x == y)                return 1;            Union(subsets, x, y);        }        return 0;    }     // Driver Code    public static void main(String[] args)    {        /* Let us create the following graph            0            | \            | \            1-----2 */         int V = 3, E = 3;        Graph graph = new Graph(V, E);         // add edge 0-1        graph.edge[0].src = 0;        graph.edge[0].dest = 1;         // add edge 1-2        graph.edge[1].src = 1;        graph.edge[1].dest = 2;         // add edge 0-2        graph.edge[2].src = 0;        graph.edge[2].dest = 2;         if (graph.isCycle(graph) == 1)            System.out.println("Graph contains cycle");        else            System.out.println(                "Graph doesn't contain cycle");    }} // This code is contributed// by ashwani khemani
# A union by rank and path compression based# program to detect cycle in a graphfrom collections import defaultdict # a structure to represent a graph  class Graph:     def __init__(self, num_of_v):        self.num_of_v = num_of_v        self.edges = defaultdict(list)     # graph is represented as an    # array of edges    def add_edge(self, u, v):        self.edges[u].append(v)  class Subset:    def __init__(self, parent, rank):        self.parent = parent        self.rank = rank # A utility function to find set of an element# node(uses path compression technique)  def find(subsets, node):    if subsets[node].parent != node:        subsets[node].parent = find(subsets, subsets[node].parent)    return subsets[node].parent # A function that does union of two sets# of u and v(uses union by rank)  def union(subsets, u, v):     # Attach smaller rank tree under root    # of high rank tree(Union by Rank)    if subsets[u].rank > subsets[v].rank:        subsets[v].parent = u    elif subsets[v].rank > subsets[u].rank:        subsets[u].parent = v     # If ranks are same, then make one as    # root and increment its rank by one    else:        subsets[v].parent = u        subsets[u].rank += 1 # The main function to check whether a given# graph contains cycle or not  def isCycle(graph):     # Allocate memory for creating sets    subsets = []     for u in range(graph.num_of_v):        subsets.append(Subset(u, 0))     # Iterate through all edges of graph,    # find sets of both vertices of every    # edge, if sets are same, then there    # is cycle in graph.    for u in graph.edges:        u_rep = find(subsets, u)         for v in graph.edges[u]:            v_rep = find(subsets, v)             if u_rep == v_rep:                return True            else:                union(subsets, u_rep, v_rep)  # Driver Codeg = Graph(3) # add edge 0-1g.add_edge(0, 1) # add edge 1-2g.add_edge(1, 2) # add edge 0-2g.add_edge(0, 2) if isCycle(g):    print('Graph contains cycle')else:    print('Graph does not contain cycle') # This code is contributed by# Sampath Kumar Surine
// A union by rank and path compression// based program to detect cycle in a graphusing System; class Graph {    public int V, E;    public Edge[] edge;     public Graph(int nV, int nE)    {        V = nV;        E = nE;        edge = new Edge[E];        for (int i = 0; i < E; i++)        {            edge[i] = new Edge();        }    }     // class to represent edge    public class Edge {        public int src, dest;    }     // class to represent Subset    class subset    {        public int parent;        public int rank;    }     // A utility function to find    // set of an element i (uses    // path compression technique)    int find(subset[] subsets, int i)    {        if (subsets[i].parent != i)            subsets[i].parent                = find(subsets, subsets[i].parent);        return subsets[i].parent;    }     // A function that does union    // of two sets of x and y    // (uses union by rank)    void Union(subset[] subsets, int x, int y)    {        int xroot = find(subsets, x);        int yroot = find(subsets, y);         if (subsets[xroot].rank < subsets[yroot].rank)            subsets[xroot].parent = yroot;        else if (subsets[yroot].rank < subsets[xroot].rank)            subsets[yroot].parent = xroot;        else {            subsets[xroot].parent = yroot;            subsets[yroot].rank++;        }    }     // The main function to check whether    // a given graph contains cycle or not    int isCycle(Graph graph)    {        int V = graph.V;        int E = graph.E;         subset[] subsets = new subset[V];        for (int v = 0; v < V; v++)        {            subsets[v] = new subset();            subsets[v].parent = v;            subsets[v].rank = 0;        }         for (int e = 0; e < E; e++)        {            int x = find(subsets, graph.edge[e].src);            int y = find(subsets, graph.edge[e].dest);            if (x == y)                return 1;            Union(subsets, x, y);        }        return 0;    }     // Driver Code    static public void Main(String[] args)    {        /* Let us create the following graph            0            | \            | \            1-----2 */         int V = 3, E = 3;        Graph graph = new Graph(V, E);         // add edge 0-1        graph.edge[0].src = 0;        graph.edge[0].dest = 1;         // add edge 1-2        graph.edge[1].src = 1;        graph.edge[1].dest = 2;         // add edge 0-2        graph.edge[2].src = 0;        graph.edge[2].dest = 2;         if (graph.isCycle(graph) == 1)            Console.WriteLine("Graph contains cycle");        else            Console.WriteLine(                "Graph doesn't contain cycle");    }} // This code is contributed// by Arnab Kundu
<script>// A union by rank and path compression// based program to detect cycle in a graph let V, E;let edge; function Graph(nV,nE){    V = nV;        E = nE;        edge = new Array(E);        for (let i = 0; i < E; i++)        {            edge[i] = new Edge();        }} // class to represent edgeclass Edge{    constructor()    {        this.src=0;        this.dest=0;    }} // class to represent Subsetclass subset{    constructor()    {        this.parent=0;        this.rank=0;    }} // A utility function to find    // set of an element i (uses    // path compression technique)function find(subsets,i){    if (subsets[i].parent != i)            subsets[i].parent                = find(subsets, subsets[i].parent);        return subsets[i].parent;} // A function that does union    // of two sets of x and y    // (uses union by rank)function Union(subsets,x,y){    let xroot = find(subsets, x);        let yroot = find(subsets, y);          if (subsets[xroot].rank < subsets[yroot].rank)            subsets[xroot].parent = yroot;        else if (subsets[yroot].rank < subsets[xroot].rank)            subsets[yroot].parent = xroot;        else {            subsets[xroot].parent = yroot;            subsets[yroot].rank++;        }} // The main function to check whether    // a given graph contains cycle or notfunction isCycle(){               let subsets = new Array(V);        for (let v = 0; v < V; v++) {              subsets[v] = new subset();            subsets[v].parent = v;            subsets[v].rank = 0;        }          for (let e = 0; e < E; e++) {            let x = find(subsets, edge[e].src);            let y = find(subsets, edge[e].dest);            if (x == y)                return 1;            Union(subsets, x, y);        }        return 0;} // Driver Code/* Let us create the following graph            0            | \            | \            1-----2 */          V = 3, E = 3;        Graph(V, E);          // add edge 0-1        edge[0].src = 0;        edge[0].dest = 1;          // add edge 1-2        edge[1].src = 1;        edge[1].dest = 2;          // add edge 0-2        edge[2].src = 0;        edge[2].dest = 2;         if (isCycle() == 1)            document.write("Graph contains cycle");        else            document.write(                "Graph doesn't contain cycle");  // This code is contributed by avanitrachhadiya2155</script>
Graph contains cycle
Related Articles : 
Union-Find Algorithm | Set 1 (Detect Cycle in a an Undirected Graph) 
Disjoint Set Data Structures (Java Implementation) 
Greedy Algorithms | Set 2 (Kruskal’s Minimum Spanning Tree Algorithm) 
Job Sequencing Problem | Set 2 (Using Disjoint Set)
DeepeshThakur
ashwani khemani
andrew1234
Sampath Kumar Surine
rahulsharma9
rutvik_56
pratham76
divyesh072019
mukesh07
avanitrachhadiya2155
mittalshubham05
amartyaghoshgfg
adityakumar129
hardikkoriintern
union-find
Graph
Graph
union-find
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Dijkstra's shortest path algorithm | Greedy Algo-7
Kruskal’s Minimum Spanning Tree Algorithm | Greedy Algo-2
Find if there is a path between two vertices in a directed graph
Detect Cycle in a Directed Graph
Introduction to Data Structures
What is Data Structure: Types, Classifications and Applications
Bellman–Ford Algorithm | DP-23
Find if there is a path between two vertices in an undirected graph
Minimum number of swaps required to sort an array
m Coloring Problem | Backtracking-5