Union-Find(=Disjoint Set Union, DSU)๋ โ์๋ก์ ์งํฉโ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ฉด์, ๋ ์์๊ฐ ๊ฐ์ ์งํฉ์ธ์ง ํ์ธ(Find)ํ๊ณ ๋ ์งํฉ์ ํฉ์น๋(Union) ์ฐ์ฐ์ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋ ์๋ฃ๊ตฌ์กฐ/์๊ณ ๋ฆฌ์ฆ์ด๋ค. ๊ฒฝ๋ก ์์ถ(Path Compression)๊ณผ ๋ญํฌ/์ฌ์ด์ฆ ๊ธฐ๋ฐ ํฉ์น๊ธฐ(Union by Rank/Size)๋ฅผ ๊ฐ์ด ์ฐ๋ฉด, ์ฐ์ฐ์ด ๊ฑฐ์ ์์ ์๊ฐ์ ๊ฐ๊น๊ฒ ๋์ํ๋ค.
๊ทธ๋ํ์์ โ์ฌ์ดํด์ด ์๊ธฐ๋์งโ ํ์ธํ๊ฑฐ๋, ์ต์ ์ ์ฅ ํธ๋ฆฌ(MST)์ธ Kruskal ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ๋ ์์ฃผ ๋ฑ์ฅํ๋ค. ํนํ Kruskal์์๋ ๊ฐ์ ์ ๊ฐ์ค์น ์์ผ๋ก ๋ณด๋ฉฐ, ๋ ์ ์ ์ด ์ด๋ฏธ ๊ฐ์ ์งํฉ์ด๋ฉด ๊ทธ ๊ฐ์ ์ ์ถ๊ฐํ ๋ ์ฌ์ดํด์ด ์๊ธฐ๋ฏ๋ก ์ ์ธํ๋ ๋ฐฉ์์ผ๋ก Union-Find๋ฅผ ์ฌ์ฉํ๋ค.
์ฌ๊ธฐ์ DSU๋ parent ๋ฐฐ์ด๋ก ํธ๋ฆฌ(ํฌ๋ ์คํธ)๋ฅผ ํํํ๋ ๋ฐฉ์์ด ์ผ๋ฐ์ ์ด๊ณ , Find๋ ํธ๋ฆฌ์ ๋ฃจํธ๊น์ง ๋ฐ๋ผ ์ฌ๋ผ๊ฐ๋ ๊ณผ์ ์ด๋ค.
์๋ ์ฝ๋๋ ์ฝ๋ฉํ ์คํธ์์ ๋ฐ๋ก ๊ฐ์ ธ๋ค ์ฐ๊ธฐ ์ข์ ํํ(0..n-1 ์ ์ )๋ก ์์ฑํ๋ค.
public class UnionFind {
private final int[] parent;
private final int[] rank; // ํธ๋ฆฌ์ ๋๋ต์ ๋์ด๋ค.
public UnionFind(int n) {
parent = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
}
// Find with path compression์ด๋ค.
public int find(int x) {
if (parent[x] == x) return x;
parent[x] = find(parent[x]); // path compression์ด๋ค.
return parent[x];
}
// Union by rank์ด๋ค.
public boolean union(int a, int b) {
int ra = find(a);
int rb = find(b);
if (ra == rb) return false;
if (rank[ra] < rank[rb]) {
parent[ra] = rb;
} else if (rank[ra] > rank[rb]) {
parent[rb] = ra;
} else {
parent[rb] = ra;
rank[ra]++;
}
return true;
}
public boolean isSameSet(int a, int b) {
return find(a) == find(b);
}
}
๋ฌด๋ฐฉํฅ ๊ทธ๋ํ์์ ๊ฐ์ (u, v)๋ฅผ ์ถ๊ฐํ๋ ค๋๋ฐ u์ v๊ฐ ์ด๋ฏธ ๊ฐ์ ์งํฉ์ด๋ฉด ๊ทธ ๊ฐ์ ์ ์ฌ์ดํด์ ๋ง๋ ๋ค.
// edges: int[][] edges = { {0,1}, {1,2}, {2,0} ... }์ด๋ค.
UnionFind uf = new UnionFind(n);
boolean hasCycle = false;
for (int[] e : edges) {
int u = e[0], v = e;
if (uf.isSameSet(u, v)) { // ์ด๋ฏธ ์ฐ๊ฒฐ๋จ => ์ถ๊ฐํ๋ฉด ์ฌ์ดํด์ด๋ค.
hasCycle = true;
break;
}
uf.union(u, v);
}