|
| 1 | +package com.thealgorithms.datastructures.disjointsetunion; |
| 2 | + |
| 3 | +/** |
| 4 | + * Disjoint Set Union (DSU) with Union by Size. |
| 5 | + * This data structure tracks a set of elements partitioned into disjoint (non-overlapping) subsets. |
| 6 | + * It supports two primary operations efficiently: |
| 7 | + * |
| 8 | + * <ul> |
| 9 | + * <li>Find: Determine which subset a particular element belongs to.</li> |
| 10 | + * <li>Union: Merge two subsets into a single subset using union by size.</li> |
| 11 | + * </ul> |
| 12 | + * |
| 13 | + * Union by size always attaches the smaller tree under the root of the larger tree. |
| 14 | + * This helps keep the tree shallow, improving the efficiency of find operations. |
| 15 | + * |
| 16 | + * @see <a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure">Disjoint Set Union (Wikipedia)</a> |
| 17 | + */ |
| 18 | +public class DisjointSetUnionBySize<T> { |
| 19 | + /** |
| 20 | + * Node class for DSU by size. |
| 21 | + * Each node keeps track of its parent and the size of the set it represents. |
| 22 | + */ |
| 23 | + public static class Node<T> { |
| 24 | + public T value; |
| 25 | + public Node<T> parent; |
| 26 | + public int size; // size of the set |
| 27 | + |
| 28 | + public Node(T value) { |
| 29 | + this.value = value; |
| 30 | + this.parent = this; |
| 31 | + this.size = 1; // initially, the set size is 1 |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | + /** |
| 36 | + * Creates a new disjoint set containing the single specified element. |
| 37 | + * @param value the element to be placed in a new singleton set |
| 38 | + * @return a node representing the new set |
| 39 | + */ |
| 40 | + public Node<T> makeSet(final T value) { |
| 41 | + return new Node<>(value); |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * Finds and returns the representative (root) of the set containing the given node. |
| 46 | + * This method applies path compression to flatten the tree structure for future efficiency. |
| 47 | + * @param node the node whose set representative is to be found |
| 48 | + * @return the representative (root) node of the set |
| 49 | + */ |
| 50 | + public Node<T> findSet(Node<T> node) { |
| 51 | + if (node != node.parent) { |
| 52 | + node.parent = findSet(node.parent); // path compression |
| 53 | + } |
| 54 | + return node.parent; |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Merges the sets containing the two given nodes using union by size. |
| 59 | + * The root of the smaller set is attached to the root of the larger set. |
| 60 | + * @param x a node in the first set |
| 61 | + * @param y a node in the second set |
| 62 | + */ |
| 63 | + public void unionSets(Node<T> x, Node<T> y) { |
| 64 | + Node<T> rootX = findSet(x); |
| 65 | + Node<T> rootY = findSet(y); |
| 66 | + |
| 67 | + if (rootX == rootY) { |
| 68 | + return; // They are already in the same set |
| 69 | + } |
| 70 | + // Union by size: attach smaller tree under the larger one |
| 71 | + if (rootX.size < rootY.size) { |
| 72 | + rootX.parent = rootY; |
| 73 | + rootY.size += rootX.size; // update size |
| 74 | + } else { |
| 75 | + rootY.parent = rootX; |
| 76 | + rootX.size += rootY.size; // update size |
| 77 | + } |
| 78 | + } |
| 79 | +} |
| 80 | +// This implementation uses union by size instead of union by rank. |
| 81 | +// The size field tracks the number of elements in each set. |
| 82 | +// When two sets are merged, the smaller set is always attached to the larger set's root. |
| 83 | +// This helps keep the tree shallow and improves the efficiency of find operations. |
0 commit comments