1+ package com .thealgorithms .graph ;
2+
3+ import java .util .*;
4+
5+ /**
6+ * Implementation of the Disjoint Set (Union-Find) data structure with path
7+ * compression
8+ * and union by rank optimizations.
9+ *
10+ * <p>
11+ * A disjoint-set data structure maintains a collection of disjoint dynamic
12+ * sets.
13+ * Each set is represented by a "representative" element. The data structure
14+ * supports
15+ * two main operations:
16+ * </p>
17+ * <ul>
18+ * <li>Find: Determine which set an element belongs to by finding the
19+ * representative</li>
20+ * <li>Union: Join two sets into a single set</li>
21+ * </ul>
22+ *
23+ * <p>
24+ * Time Complexity:
25+ * </p>
26+ * <ul>
27+ * <li>Find: O(α(n)) amortized</li>
28+ * <li>Union: O(α(n)) amortized</li>
29+ * </ul>
30+ * <p>
31+ * where α(n) is the inverse Ackermann function, which grows extremely slowly.
32+ * For all practical values of n, α(n) ≤ 4.
33+ * </p>
34+ *
35+ * <p>
36+ * Space Complexity: O(n) where n is the number of elements
37+ * </p>
38+ *
39+ * <p>
40+ * Applications:
41+ * </p>
42+ * <ul>
43+ * <li>Kruskal's algorithm for minimum spanning trees</li>
44+ * <li>Finding connected components in graphs</li>
45+ * <li>Online dynamic connectivity</li>
46+ * <li>Least common ancestor in trees</li>
47+ * </ul>
48+ */
49+ public class DisjointSet {
50+ private final int [] parent ;
51+ private final int [] rank ;
52+ private final int size ;
53+ private int numSets ; // Tracks number of disjoint sets
54+
55+ /**
56+ * Initializes a disjoint set data structure with elements from 0 to size-1.
57+ *
58+ * @param size number of elements
59+ * @throws IllegalArgumentException if size is negative
60+ */
61+ public DisjointSet (int size ) {
62+ if (size < 0 ) {
63+ throw new IllegalArgumentException ("Size must be non-negative" );
64+ }
65+ this .size = size ;
66+ this .numSets = size ;
67+ parent = new int [size ];
68+ rank = new int [size ];
69+
70+ // Initialize each element as its own set
71+ for (int i = 0 ; i < size ; i ++) {
72+ parent [i ] = i ;
73+ rank [i ] = 0 ;
74+ }
75+ }
76+
77+ /**
78+ * Finds the representative of the set containing element x.
79+ * Uses path compression to optimize future queries.
80+ *
81+ * @param x element to find representative for
82+ * @return representative of x's set
83+ * @throws IllegalArgumentException if x is out of bounds
84+ */
85+ public int find (int x ) {
86+ if (x < 0 || x >= size ) {
87+ throw new IllegalArgumentException ("Element out of bounds: " + x );
88+ }
89+
90+ // Path compression: Make all nodes on path point directly to root
91+ if (parent [x ] != x ) {
92+ parent [x ] = find (parent [x ]);
93+ }
94+ return parent [x ];
95+ }
96+
97+ /**
98+ * Merges the sets containing elements x and y.
99+ * Uses union by rank to keep the tree shallow.
100+ *
101+ * @param x first element
102+ * @param y second element
103+ * @return true if x and y were in different sets, false if they were already in
104+ * same set
105+ * @throws IllegalArgumentException if either element is out of bounds
106+ */
107+ public boolean union (int x , int y ) {
108+ int rootX = find (x );
109+ int rootY = find (y );
110+
111+ if (rootX == rootY ) {
112+ return false ; // Already in same set
113+ }
114+
115+ // Union by rank: Attach smaller rank tree under root of higher rank tree
116+ if (rank [rootX ] < rank [rootY ]) {
117+ parent [rootX ] = rootY ;
118+ } else if (rank [rootX ] > rank [rootY ]) {
119+ parent [rootY ] = rootX ;
120+ } else {
121+ parent [rootY ] = rootX ;
122+ rank [rootX ]++;
123+ }
124+
125+ numSets --;
126+ return true ;
127+ }
128+
129+ /**
130+ * Checks if two elements are in the same set.
131+ *
132+ * @param x first element
133+ * @param y second element
134+ * @return true if x and y are in the same set
135+ * @throws IllegalArgumentException if either element is out of bounds
136+ */
137+ public boolean connected (int x , int y ) {
138+ return find (x ) == find (y );
139+ }
140+
141+ /**
142+ * Returns the current number of disjoint sets.
143+ *
144+ * @return number of disjoint sets
145+ */
146+ public int getNumSets () {
147+ return numSets ;
148+ }
149+
150+ /**
151+ * Returns the size of the set containing element x.
152+ *
153+ * @param x element to find set size for
154+ * @return size of x's set
155+ * @throws IllegalArgumentException if x is out of bounds
156+ */
157+ public int getSetSize (int x ) {
158+ int root = find (x );
159+ int count = 0 ;
160+ for (int i = 0 ; i < size ; i ++) {
161+ if (find (i ) == root ) {
162+ count ++;
163+ }
164+ }
165+ return count ;
166+ }
167+
168+ /**
169+ * Returns all elements in the same set as x.
170+ *
171+ * @param x element to find set members for
172+ * @return list of all elements in x's set
173+ * @throws IllegalArgumentException if x is out of bounds
174+ */
175+ public List <Integer > getSetMembers (int x ) {
176+ int root = find (x );
177+ List <Integer > members = new ArrayList <>();
178+ for (int i = 0 ; i < size ; i ++) {
179+ if (find (i ) == root ) {
180+ members .add (i );
181+ }
182+ }
183+ return members ;
184+ }
185+ }
0 commit comments