Dsu Union Rank

/**
 * @file
 * @brief [DSU (Disjoint
 * sets)](https://en.wikipedia.org/wiki/Disjoint-set-data_structure)
 * @details
 * dsu : It is a very powerful data structure which keeps track of different
 * clusters(sets) of elements, these sets are disjoint(doesnot have a common
 * element). Disjoint sets uses cases : for finding connected components in a
 * graph, used in Kruskal's algorithm for finding Minimum Spanning tree.
 * Operations that can be performed:
 * 1) UnionSet(i,j): add(element i and j to the set)
 * 2) findSet(i): returns the representative of the set to which i belogngs to.
 * 3) getParents(i): prints the parent of i and so on and so forth.
 * Below is the class-based approach which uses the heuristic of union-ranks.
 * Using union-rank in findSet(i),we are able to get to the representative of i
 * in slightly delayed O(logN) time but it allows us to keep tracks of the
 * parent of i.
 * @author [AayushVyasKIIT](https://github.com/AayushVyasKIIT)
 * @see dsu_path_compression.cpp
 */

#include <cassert>   /// for assert
#include <iostream>  /// for IO operations
#include <vector>    /// for std::vector

using std::cout;
using std::endl;
using std::vector;

/**
 * @brief Disjoint sets union data structure, class based representation.
 * @param n number of elements
 */
class dsu {
 private:
    vector<uint64_t> p;        ///< keeps track of the parent of ith element
    vector<uint64_t> depth;    ///< tracks the depth(rank) of i in the tree
    vector<uint64_t> setSize;  ///< size of each chunk(set)
 public:
    /**
     * @brief constructor for initialising all data members
     * @param n number of elements
     */
    explicit dsu(uint64_t n) {
        p.assign(n, 0);
        /// initially all of them are their own parents
        depth.assign(n, 0);
        setSize.assign(n, 0);
        for (uint64_t i = 0; i < n; i++) {
            p[i] = i;
            depth[i] = 0;
            setSize[i] = 1;
        }
    }
    /**
     * @brief Method to find the representative of the set to which i belongs
     * to, T(n) = O(logN)
     * @param i element of some set
     * @returns representative of the set to which i belongs to
     */
    uint64_t findSet(uint64_t i) {
        /// using union-rank
        while (i != p[i]) {
            i = p[i];
        }
        return i;
    }
    /**
     * @brief Method that combines two disjoint sets to which i and j belongs to
     * and make a single set having a common representative.
     * @param i element of some set
     * @param j element of some set
     * @returns void
     */
    void unionSet(uint64_t i, uint64_t j) {
        /// checks if both belongs to same set or not
        if (isSame(i, j)) {
            return;
        }
        /// we find representative of the i and j
        uint64_t x = findSet(i);
        uint64_t y = findSet(j);

        /// always keeping the min as x
        /// in order to create a shallow tree
        if (depth[x] > depth[y]) {
            std::swap(x, y);
        }
        /// making the shallower tree, root parent of the deeper root
        p[x] = y;

        /// if same depth, then increase one's depth
        if (depth[x] == depth[y]) {
            depth[y]++;
        }
        /// total size of the resultant set
        setSize[y] += setSize[x];
    }
    /**
     * @brief A utility function which check whether i and j belongs to same set
     * or not
     * @param i element of some set
     * @param j element of some set
     * @returns `true` if element i and j are in same set
     * @returns `false` if element i and j are not in same set
     */
    bool isSame(uint64_t i, uint64_t j) {
        if (findSet(i) == findSet(j)) {
            return true;
        }
        return false;
    }
    /**
     * @brief Method to print all the parents of i, or the path from i to
     * representative.
     * @param i element of some set
     * @returns void
     */
    vector<uint64_t> getParents(uint64_t i) {
        vector<uint64_t> ans;
        while (p[i] != i) {
            ans.push_back(i);
            i = p[i];
        }
        ans.push_back(i);
        return ans;
    }
};
/**
 * @brief Self-implementations, 1st test
 * @returns void
 */
static void test1() {
    /* checks the parents in the resultant structures */
    uint64_t n = 10;   ///< number of elements
    dsu d(n + 1);      ///< object of class disjoint sets
    d.unionSet(2, 1);  ///< performs union operation on 1 and 2
    d.unionSet(1, 4);
    d.unionSet(8, 1);
    d.unionSet(3, 5);
    d.unionSet(5, 6);
    d.unionSet(5, 7);
    d.unionSet(9, 10);
    d.unionSet(2, 10);
    // keeping track of the changes using parent pointers
    vector<uint64_t> ans = {7, 5};
    for (uint64_t i = 0; i < ans.size(); i++) {
        assert(d.getParents(7).at(i) ==
               ans[i]);  // makes sure algorithm works fine
    }
    cout << "1st test passed!" << endl;
}
/**
 * @brief Self-implementations, 2nd test
 * @returns void
 */
static void test2() {
    // checks the parents in the resultant structures
    uint64_t n = 10;   ///< number of elements
    dsu d(n + 1);      ///< object of class disjoint sets
    d.unionSet(2, 1);  /// performs union operation on 1 and 2
    d.unionSet(1, 4);
    d.unionSet(8, 1);
    d.unionSet(3, 5);
    d.unionSet(5, 6);
    d.unionSet(5, 7);
    d.unionSet(9, 10);
    d.unionSet(2, 10);

    /// keeping track of the changes using parent pointers
    vector<uint64_t> ans = {2, 1, 10};
    for (uint64_t i = 0; i < ans.size(); i++) {
        assert(d.getParents(2).at(i) ==
               ans[i]);  /// makes sure algorithm works fine
    }
    cout << "2nd test passed!" << endl;
}
/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test1();  // run 1st test case
    test2();  // run 2nd test case

    return 0;
}
Algerlogo

Β© Alger 2022

About us

We are a group of programmers helping each other build new things, whether it be writing complex encryption programs, or simple ciphers. Our goal is to work together to document and model beautiful, helpful and interesting algorithms using code. We are an open-source community - anyone can contribute. We check each other's work, communicate and collaborate to solve problems. We strive to be welcoming, respectful, yet make sure that our code follows the latest programming guidelines.