open_hypergraphs/array/vec/
connected_components.rs1use std::collections::HashMap;
2
3struct UnionFind {
4 parent: Vec<usize>,
6 rank: Vec<usize>,
8}
9
10impl UnionFind {
11 fn new(n: usize) -> Self {
13 Self {
14 parent: (0..n).collect(),
15 rank: vec![0; n],
16 }
17 }
18
19 fn find(&mut self, x: usize) -> usize {
22 if self.parent[x] != x {
23 self.parent[x] = self.find(self.parent[x]);
24 }
25 self.parent[x]
26 }
27
28 fn union(&mut self, x: usize, y: usize) {
31 let root_x = self.find(x);
32 let root_y = self.find(y);
33 if root_x != root_y {
34 #[allow(clippy::comparison_chain)]
35 if self.rank[root_x] > self.rank[root_y] {
36 self.parent[root_y] = root_x;
37 } else if self.rank[root_x] < self.rank[root_y] {
38 self.parent[root_x] = root_y;
39 } else {
40 self.parent[root_y] = root_x;
41 self.rank[root_x] += 1;
42 }
43 }
44 }
45}
46
47pub fn connected_components(sources: &[usize], targets: &[usize], n: usize) -> (Vec<usize>, usize) {
55 assert_eq!(sources.len(), targets.len());
57 assert!(n > 0 || sources.is_empty());
59 let mut uf = UnionFind::new(n);
60
61 for (u, v) in sources.iter().zip(targets) {
63 uf.union(*u, *v);
64 }
65
66 let node_to_other_node = (0..n).map(|i| uf.find(i)).collect::<Vec<_>>();
68 let (node_to_component_number, num_components) = to_dense(&node_to_other_node);
69 (node_to_component_number, num_components)
70}
71
72#[must_use]
73pub fn to_dense(sparse: &[usize]) -> (Vec<usize>, usize) {
76 let mut representative_nodes: HashMap<usize, usize> = HashMap::new();
77 let mut num_components = 0;
78 let mut dense: Vec<usize> = Vec::with_capacity(sparse.len());
79 for a in sparse {
80 if let Some(found_component_number) = representative_nodes.get(a) {
81 dense.push(*found_component_number);
82 } else {
83 representative_nodes.insert(*a, num_components);
84 dense.push(num_components);
85 num_components += 1;
86 }
87 }
88 (dense, num_components)
89}
90
91#[cfg(test)]
92mod test {
93 use proptest::prelude::{Just, Strategy};
94 use proptest::{prop_assert_eq, proptest};
95
96 use super::{connected_components, to_dense};
97
98 #[test]
99 fn to_dense_example() {
100 assert_eq!(to_dense(&[0, 2, 5, 5, 7]), (vec![0, 1, 2, 2, 3], 4));
101 }
102
103 #[test]
104 fn small_graph_components() {
105 let components = connected_components(&[0, 1, 3], &[1, 2, 4], 5);
106 let expected = (vec![0, 0, 0, 1, 1], 2);
107
108 assert_eq!(components, expected);
109 }
110
111 fn edges_strategy() -> impl Strategy<Value = (Vec<usize>, Vec<usize>, usize)> {
112 (1_usize..100).prop_flat_map(|n| {
113 (Just(n), (0..(n + 2) / 2)).prop_flat_map(|(n, e)| {
114 (
115 proptest::collection::vec(0..n, e),
116 proptest::collection::vec(0..n, e),
117 Just(n),
118 )
119 })
120 })
121 }
122
123 proptest! {
124 #[test]
125 fn general_components((sources,targets,num_nodes) in edges_strategy()) {
126 let (mut z, component_count) = connected_components(&sources, &targets, num_nodes);
127 prop_assert_eq!(z.len(),num_nodes);
128 for (a,b) in sources.into_iter().zip(targets) {
130 prop_assert_eq!(z[a],z[b]);
131 }
132 let num_components = z.iter().max().copied().expect("At least 1 node") + 1;
134 prop_assert_eq!(num_components,component_count);
135 z.sort_unstable();
137 z.dedup();
138 prop_assert_eq!(z, (0..num_components).collect::<Vec<_>>());
139 }
140 }
141}