Open In App

Union By Rank and Path Compression in Union-Find Algorithm

Last Updated : 24 Mar, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In the previous post, we introduced union find algorithm and used it to detect cycles in a graph. We used the following union() and find() operations for subsets.

C++




// Naive implementation of find
int 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;
}


Java




// Naive implementation of find
static 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


Python3




# Naive implementation of find
def 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


C#




// Naive implementation of find
static 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


Javascript




<script>
 
// Naive implementation of find
function 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 the worst case. The idea is to always attach a 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 the path compression technique (we have discussed it below) is used, then the rank is not always equal to height. Also, the 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 -path compression with the union by rank/size, the time complexity will reach nearly constant time. It turns out, that the final amortized time complexity is O(α(n)), where α(n) is the inverse Ackermann function, which grows very steadily (it does not even exceed for n<10600  approximately).

Following is union by rank and path compression-based implementation to find a cycle in a graph. 

C++




// 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 graph
struct Edge {
    int src, dest;
};
 
// a structure to represent a graph
struct 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 edges
struct 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 not
int 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 code
int 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)


C




// 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 graph
typedef struct Edge {
    int src, dest;
}Edge;
 
// a structure to represent a graph
typedef 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 edges
Graph* 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 not
int 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 code
int 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)


Java




// A union by rank and path compression
// based program to detect cycle in a graph
class 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


Python3




# A union by rank and path compression based
# program to detect cycle in a graph
from 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 Code
g = Graph(3)
 
# add edge 0-1
g.add_edge(0, 1)
 
# add edge 1-2
g.add_edge(1, 2)
 
# add edge 0-2
g.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


C#




// A union by rank and path compression
// based program to detect cycle in a graph
using 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


Javascript




<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 edge
class Edge
{
    constructor()
    {
        this.src=0;
        this.dest=0;
    }
}
 
// class to represent Subset
class 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 not
function 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>


Output

Graph contains cycle

Time complexity: O(ElogV) where E is the number of edges in the graph and V is the number of vertices. 

Space complexity: O(V), where V is the number of vertices. This is because we are using an array of subsets to store the representative elements of each vertex, and the size of this array is proportional to the number of vertices.

Related Articles : 



Previous Article
Next Article

Similar Reads

Union-Find Algorithm | (Union By Rank and Find by Optimized Path Compression)
Check whether a given graph contains a cycle or not. Example: Input: Output: Graph contains Cycle. Input: Output: Graph does not contain Cycle. Prerequisites: Disjoint Set (Or Union-Find), Union By Rank and Path CompressionWe have already discussed union-find to detect cycle. Here we discuss find by path compression, where it is slightly modified t
13 min read
Number of players whose rank is equal to or less than a given cutoff rank
Given an array arr[] consisting of N integers and an integer R, denoting the cutoff rank, the task is to count the number of array elements with rank at most R such that the equal array element are ranked the same and distinct array elements are ranked based on their positions in the array arr[]. Examples: Input: arr[] = {100, 50, 50, 25}, R = 3Out
12 min read
Shannon-Fano Algorithm for Data Compression
DATA COMPRESSION AND ITS TYPES Data Compression, also known as source coding, is the process of encoding or converting data in such a way that it consumes less memory space. Data compression reduces the number of resources required to store and transmit data.  It can be done in two ways- lossless compression and lossy compression. Lossy compression
15+ min read
Text File Compression And Decompression Using Huffman Coding
Text files can be compressed to make them smaller and faster to send, and unzipping files on devices has a low overhead. The process of encoding involves changing the representation of a file so that the (binary) compressed output takes less space to store and takes less time to transmit while retaining the ability to reconstruct the original file
14 min read
Introduction to Disjoint Set (Union-Find Algorithm)
What is a Disjoint set data structure?Two sets are called disjoint sets if they don't have any element in common, the intersection of sets is a null set. A data structure that stores non overlapping or disjoint subset of elements is called disjoint set data structure. The disjoint set data structure supports following operations: Adding new sets to
15+ min read
Coordinate Compression
Given an array, arr[] containing Integers from 1 to 10^12 compress them to numbers ranging from 1 to 10^6. size of the array is ? 10^6. Examples: Input: arr[] = {100000000000, 100000000001, 100000000002, 999999999999}Output: arr[] = {100000, 100000, 100000, 999999, 0, 1, 2, 999999}Explanation: Every element of the new array is in the range 1 to 10^
6 min read
Compression of 2D Segment Tree in Python
Compressing a 2D segment tree in Python involves reducing memory usage by storing only necessary information. This can be achieved by employing various techniques such as segment tree compression and lazy propagation. Let's create a tutorial on compressing a 2D segment tree in Python. A 2D segment tree is a data structure used for efficient queryin
5 min read
Find the path from root to a vertex such that all K vertices belongs to this path or 1 distance away
Given a tree consisting of N vertices numbered from 1 to N rooted at vertex 1. You are given Q queries. Each query consists of the set of K distinct vertices vi[1], vi[2],…, vi[K]. The task is to determine if there is a path from the root to some vertex X such that each of the given K vertices either belongs to this path or has the distance 1 to so
10 min read
Program to find Circuit Rank of an Undirected Graph
Given the number of Vertices and the number of Edges of an Undirected Graph. The task is to determine the Circuit rank. Circuit Rank: The Circuit rank of an undirected graph is defined as the minimum number of edges that must be removed from the graph to break all of its cycles, converting it into a tree or forest. Examples: Input :Edges = 7 , Vert
4 min read
Find relative rank of each element in array
Given an array A[] of N integers, the task is to find the relative rank for each element in the given array. The relative rank for each element in the array is the count of elements which is greater than the current element in the Longest Increasing Subsequence from the current element. Examples: Input: A[] = {8, 16, 5, 6, 9}, N = 5Output: {1, 0, 2
8 min read
Article Tags :
Practice Tags :
three90RightbarBannerImg