Skip to main content

atcoder/lib/data_structure/
union_find.rs

1//! 素集合データ構造(Disjoint Set Union)です。
2
3/// `0..n` の要素を互いに素な集合へ分割して管理します。
4///
5/// 経路圧縮とサイズによる併合を使用します。各操作の償却計算量は `O(α(N))` です。
6/// 添字は 0-indexed で、範囲外の添字を渡すと panic します。
7///
8/// # Examples
9///
10/// ```
11/// use atcoder::data_structure::union_find::UnionFind;
12///
13/// let mut dsu = UnionFind::new(4);
14/// assert!(dsu.merge(0, 1));
15/// assert!(dsu.same(0, 1));
16/// assert_eq!(dsu.size(0), 2);
17/// assert_eq!(dsu.group_count(), 3);
18/// ```
19#[derive(Debug, Clone)]
20pub struct UnionFind {
21    parents: Vec<isize>,
22    group_count: usize,
23}
24
25impl UnionFind {
26    /// `0..n` の各要素を含む単一要素集合を作ります。計算量・メモリ量は `O(N)` です。
27    pub fn new(n: usize) -> Self {
28        Self {
29            parents: vec![-1; n],
30            group_count: n,
31        }
32    }
33
34    /// `x` の属する集合の代表元を返します。経路圧縮を行い、償却計算量は `O(α(N))` です。
35    /// `x >= n` なら panic します。
36    pub fn find(&mut self, x: usize) -> usize {
37        if self.parents[x] < 0 {
38            x
39        } else {
40            let root = self.find(self.parents[x] as usize);
41            self.parents[x] = root as isize;
42            root
43        }
44    }
45
46    /// `x` と `y` の集合を併合します。
47    ///
48    /// 新しく併合した場合は `true`、既に同じ集合なら `false` を返します。
49    /// 償却計算量は `O(α(N))` です。`x >= n` または `y >= n` なら panic します。
50    pub fn merge(&mut self, x: usize, y: usize) -> bool {
51        let mut x = self.find(x);
52        let mut y = self.find(y);
53        if x == y {
54            return false;
55        }
56        if self.parents[x] > self.parents[y] {
57            std::mem::swap(&mut x, &mut y);
58        }
59        self.parents[x] += self.parents[y];
60        self.parents[y] = x as isize;
61        self.group_count -= 1;
62        true
63    }
64
65    /// `x` と `y` が同じ集合なら `true` を返します。代表元の検索を行うため、
66    /// 償却計算量は `O(α(N))` です。`x >= n` または `y >= n` なら panic します。
67    pub fn same(&mut self, x: usize, y: usize) -> bool {
68        self.find(x) == self.find(y)
69    }
70
71    /// `x` の属する集合の要素数を返します。代表元の検索を行うため、
72    /// 償却計算量は `O(α(N))` です。`x >= n` なら panic します。
73    pub fn size(&mut self, x: usize) -> usize {
74        let root = self.find(x);
75        (-self.parents[root]) as usize
76    }
77
78    /// 現在の集合数を返します。計算量は `O(1)` です。
79    pub const fn group_count(&self) -> usize {
80        self.group_count
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::UnionFind;
87
88    #[test]
89    fn merges_components() {
90        let mut dsu = UnionFind::new(3);
91        assert!(dsu.merge(0, 2));
92        assert!(!dsu.merge(2, 0));
93        assert_eq!(dsu.size(2), 2);
94        assert_eq!(dsu.group_count(), 2);
95    }
96}