Skip to content

Add tests, remove main in UnionFind #5678

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,7 @@
* [SortOrderAgnosticBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java)
* [SquareRootBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java)
* [TestSearchInARowAndColWiseSortedMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java)
* [UnionFindTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/UnionFindTest.java)
* sorts
* [BeadSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BeadSortTest.java)
* [BinaryInsertionSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BinaryInsertionSortTest.java)
Expand Down
71 changes: 44 additions & 27 deletions src/main/java/com/thealgorithms/searches/UnionFind.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@
import java.util.Arrays;
import java.util.List;

/**
* The Union-Find data structure, also known as Disjoint Set Union (DSU),
* is a data structure that tracks a set of elements partitioned into
* disjoint (non-overlapping) subsets. It supports two main operations:
*
* 1. **Find**: Determine which subset a particular element is in.
* 2. **Union**: Join two subsets into a single subset.
*
* This implementation uses path compression in the `find` operation
* and union by rank in the `union` operation for efficiency.
*/
public class UnionFind {

private final int[] p;
private final int[] r;
private final int[] p; // Parent array
private final int[] r; // Rank array

/**
* Initializes a Union-Find data structure with n elements.
* Each element is its own parent initially.
*
* @param n the number of elements
*/
public UnionFind(int n) {
p = new int[n];
r = new int[n];
Expand All @@ -18,19 +35,33 @@ public UnionFind(int n) {
}
}

/**
* Finds the root of the set containing the element i.
* Uses path compression to flatten the structure.
*
* @param i the element to find
* @return the root of the set
*/
public int find(int i) {
int parent = p[i];

if (i == parent) {
return i;
}

// Path compression
final int result = find(parent);
p[i] = result;

return result;
}

/**
* Unites the sets containing elements x and y.
* Uses union by rank to attach the smaller tree under the larger tree.
*
* @param x the first element
* @param y the second element
*/
public void union(int x, int y) {
int r0 = find(x);
int r1 = find(y);
Expand All @@ -39,6 +70,7 @@ public void union(int x, int y) {
return;
}

// Union by rank
if (r[r0] > r[r1]) {
p[r1] = r0;
} else if (r[r1] > r[r0]) {
Expand All @@ -49,39 +81,24 @@ public void union(int x, int y) {
}
}

/**
* Counts the number of disjoint sets.
*
* @return the number of disjoint sets
*/
public int count() {
List<Integer> parents = new ArrayList<>();
for (int i = 0; i < p.length; i++) {
if (!parents.contains(find(i))) {
parents.add(find(i));
int root = find(i);
if (!parents.contains(root)) {
parents.add(root);
}
}
return parents.size();
}

@Override
public String toString() {
return "p " + Arrays.toString(p) + " r " + Arrays.toString(r) + "\n";
}

// Tests
public static void main(String[] args) {
UnionFind uf = new UnionFind(5);
System.out.println("init /w 5 (should print 'p [0, 1, 2, 3, 4] r [0, 0, 0, 0, 0]'):");
System.out.println(uf);

uf.union(1, 2);
System.out.println("union 1 2 (should print 'p [0, 1, 1, 3, 4] r [0, 1, 0, 0, 0]'):");
System.out.println(uf);

uf.union(3, 4);
System.out.println("union 3 4 (should print 'p [0, 1, 1, 3, 3] r [0, 1, 0, 1, 0]'):");
System.out.println(uf);

uf.find(4);
System.out.println("find 4 (should print 'p [0, 1, 1, 3, 3] r [0, 1, 0, 1, 0]'):");
System.out.println(uf);

System.out.println("count (should print '3'):");
System.out.println(uf.count());
}
}
81 changes: 81 additions & 0 deletions src/test/java/com/thealgorithms/searches/UnionFindTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.thealgorithms.searches;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class UnionFindTest {
private UnionFind uf;

@BeforeEach
void setUp() {
uf = new UnionFind(10); // Initialize with 10 elements
}

@Test
void testInitialState() {
// Verify that each element is its own parent and rank is 0
assertEquals("p [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] r [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", uf.toString());
assertEquals(10, uf.count(), "Initial count of disjoint sets should be 10.");
}

@Test
void testUnionOperation() {
uf.union(0, 1);
uf.union(1, 2);
assertEquals(8, uf.count(), "Count should decrease after unions.");
assertEquals(0, uf.find(2), "Element 2 should point to root 0 after unions.");
}

@Test
void testUnionWithRank() {
uf.union(0, 1);
uf.union(1, 2); // Make 0 the root of 2
uf.union(3, 4);
uf.union(4, 5); // Make 3 the root of 5
uf.union(0, 3); // Union two trees

assertEquals(5, uf.count(), "Count should decrease after unions.");
assertEquals(0, uf.find(5), "Element 5 should point to root 0 after unions.");
}

@Test
void testFindOperation() {
uf.union(2, 3);
uf.union(4, 5);
uf.union(3, 5); // Connect 2-3 and 4-5

assertEquals(2, uf.find(3), "Find operation should return the root of the set.");
assertEquals(2, uf.find(5), "Find operation should return the root of the set.");
}

@Test
void testCountAfterMultipleUnions() {
uf.union(0, 1);
uf.union(2, 3);
uf.union(4, 5);
uf.union(1, 3); // Connect 0-1-2-3
uf.union(5, 6);

assertEquals(5, uf.count(), "Count should reflect the number of disjoint sets after multiple unions.");
}

@Test
void testNoUnion() {
assertEquals(10, uf.count(), "Count should remain 10 if no unions are made.");
}

@Test
void testUnionSameSet() {
uf.union(1, 2);
uf.union(1, 2); // Union same elements again

assertEquals(9, uf.count(), "Count should not decrease if union is called on the same set.");
}

@Test
void testFindOnSingleElement() {
assertEquals(7, uf.find(7), "Find on a single element should return itself.");
}
}