Skip to content

Commit 6997e81

Browse files
authored
Merge pull request #6 from harry0000/dsu
Add DSU
2 parents 1969e78 + 04d391b commit 6997e81

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package io.github.acl4s
2+
3+
import scala.collection.mutable
4+
5+
/**
6+
* Implement (union by size) + (path compression)
7+
* Reference:
8+
* Zvi Galil and Giuseppe F. Italiano,
9+
* Data structures and algorithms for disjoint set union problems
10+
*
11+
* @param n
12+
*/
13+
case class Dsu(n: Int) {
14+
15+
/**
16+
* root node: -1 * component size
17+
* otherwise: parent
18+
*/
19+
private val parentOrSize: Array[Int] = Array.fill(n)(-1)
20+
21+
def merge(a: Int, b: Int): Int = {
22+
assert(0 <= a && a < n)
23+
assert(0 <= b && b < n)
24+
var x = leader(a)
25+
var y = leader(b)
26+
if (x == y) { return x }
27+
if (-parentOrSize(x) < -parentOrSize(y)) {
28+
// std::swap(x, y);
29+
val z = x
30+
x = y
31+
y = z
32+
}
33+
parentOrSize(x) += parentOrSize(y)
34+
parentOrSize(y) = x
35+
x
36+
}
37+
38+
def same(a: Int, b: Int): Boolean = {
39+
assert(0 <= a && a < n)
40+
assert(0 <= b && b < n)
41+
leader(a) == leader(b)
42+
}
43+
44+
def leader(a: Int): Int = {
45+
assert(0 <= a && a < n)
46+
if (parentOrSize(a) < 0) {
47+
a
48+
} else {
49+
parentOrSize(a) = leader(parentOrSize(a))
50+
parentOrSize(a)
51+
}
52+
}
53+
54+
def size(a: Int): Int = {
55+
assert(0 <= a && a < n)
56+
-parentOrSize(leader(a))
57+
}
58+
59+
def groups(): Seq[Seq[Int]] = {
60+
val leader_buf = new Array[Int](n)
61+
val group_size = new Array[Int](n)
62+
(0 until n).foreach(i => {
63+
leader_buf(i) = leader(i)
64+
group_size(leader_buf(i)) += 1
65+
})
66+
67+
val result = new Array[mutable.ArrayBuffer[Int]](n)
68+
(0 until n).foreach(i => {
69+
result(i) = new mutable.ArrayBuffer(group_size(i))
70+
})
71+
(0 until n).foreach(i => {
72+
result(leader_buf(i)) += i
73+
})
74+
75+
result.collect {
76+
case buf if buf.nonEmpty => buf.toSeq
77+
}.toSeq
78+
}
79+
80+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.github.acl4s
2+
3+
class DsuSuite extends munit.FunSuite {
4+
5+
/**
6+
* @see https://atcoder.jp/contests/practice2/tasks/practice2_a
7+
*/
8+
test("AtCoder Library Practice Contest A - Disjoint Set Union") {
9+
val uf = Dsu(4)
10+
11+
assertEquals(uf.same(0, 1), false)
12+
13+
uf.merge(0, 1)
14+
uf.merge(2, 3)
15+
16+
assertEquals(uf.same(0, 1), true)
17+
assertEquals(uf.same(1, 2), false)
18+
assertEquals(uf.groups(), Seq(Seq(0, 1), Seq(2, 3)))
19+
20+
uf.merge(0, 2)
21+
22+
assertEquals(uf.same(1, 3), true)
23+
assertEquals(uf.groups(), Seq(Seq(0, 1, 2, 3)))
24+
}
25+
26+
test("zero") {
27+
val uf = Dsu(0)
28+
29+
assertEquals(uf.groups(), Seq())
30+
}
31+
32+
test("simple") {
33+
val uf = Dsu(2)
34+
35+
assertEquals(uf.same(0, 1), false)
36+
37+
val x = uf.merge(0, 1)
38+
assertEquals(uf.leader(0), x)
39+
assertEquals(uf.leader(1), x)
40+
assertEquals(uf.same(0, 1), true)
41+
assertEquals(uf.size(0), 2)
42+
}
43+
44+
test("line") {
45+
val n = 500_000
46+
val uf = Dsu(n)
47+
48+
(0 until n - 1).foreach(i => {
49+
uf.merge(i, i + 1)
50+
})
51+
52+
assertEquals(uf.size(0), n)
53+
assertEquals(uf.groups().size, 1)
54+
}
55+
56+
test("line reverse") {
57+
val n = 500_000
58+
val uf = Dsu(n)
59+
60+
(0 until n - 1).reverse.foreach(i => {
61+
uf.merge(i, i + 1)
62+
})
63+
64+
assertEquals(uf.size(0), n)
65+
assertEquals(uf.groups().size, 1)
66+
}
67+
68+
}

0 commit comments

Comments
 (0)