|
| 1 | +//! Find cliques in an undirected graph. |
| 2 | +
|
| 3 | +use std::collections::HashSet; |
| 4 | +use std::hash::Hash; |
| 5 | + |
| 6 | +/// Algorithm for finding all maximal cliques in an undirected graph. |
| 7 | +/// That is, it lists all subsets of vertices with the two properties that each pair of vertices in |
| 8 | +/// one of the listed subsets is connected by an edge, and no listed subset can have |
| 9 | +/// any additional vertices added to it while preserving its complete connectivity. |
| 10 | +/// [Bron-Kerbosch algorithm](https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm). |
| 11 | +/// |
| 12 | +/// |
| 13 | +/// - `vertices` is the list of all nodes. |
| 14 | +/// - `connected` returns true if the two given node is connected. |
| 15 | +/// - return a list of cliques. |
| 16 | +pub fn maximal_cliques_collect<N, FN, IN>(vertices: IN, connected: &mut FN) -> Vec<HashSet<N>> |
| 17 | +where |
| 18 | + N: Eq + Hash + Clone, |
| 19 | + FN: FnMut(&N, &N) -> bool, |
| 20 | + IN: IntoIterator<Item = N>, |
| 21 | +{ |
| 22 | + let mut result = Vec::new(); |
| 23 | + let mut consumer = |n: &HashSet<N>| result.push(n.to_owned()); |
| 24 | + let mut remaining_nodes: HashSet<N> = vertices.into_iter().collect::<HashSet<_>>(); |
| 25 | + bron_kerbosch( |
| 26 | + connected, |
| 27 | + &HashSet::new(), |
| 28 | + &mut remaining_nodes, |
| 29 | + &mut HashSet::new(), |
| 30 | + &mut consumer, |
| 31 | + ); |
| 32 | + result |
| 33 | +} |
| 34 | + |
| 35 | +/// Algorithm for finding all maximal cliques in an undirected graph. |
| 36 | +/// That is, it lists all subsets of vertices with the two properties that each pair of vertices in |
| 37 | +/// one of the listed subsets is connected by an edge, and no listed subset can have |
| 38 | +/// any additional vertices added to it while preserving its complete connectivity. |
| 39 | +/// [Bron-Kerbosch algorithm](https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm). |
| 40 | +/// |
| 41 | +/// |
| 42 | +/// - `vertices` is the list of all nodes. |
| 43 | +/// - `connected` returns true if the two given node is connected. |
| 44 | +/// - 'consumer' function which called for each clique. |
| 45 | +/// |
| 46 | +pub fn maximal_cliques<N, FN, IN, CO>(vertices: IN, connected: &mut FN, consumer: &mut CO) |
| 47 | +where |
| 48 | + N: Eq + Hash + Clone, |
| 49 | + FN: FnMut(&N, &N) -> bool, |
| 50 | + IN: IntoIterator<Item = N>, |
| 51 | + CO: FnMut(&HashSet<N>), |
| 52 | +{ |
| 53 | + let mut remaining_nodes: HashSet<N> = vertices.into_iter().collect(); |
| 54 | + bron_kerbosch( |
| 55 | + connected, |
| 56 | + &HashSet::new(), |
| 57 | + &mut remaining_nodes, |
| 58 | + &mut HashSet::new(), |
| 59 | + consumer, |
| 60 | + ); |
| 61 | +} |
| 62 | + |
| 63 | +fn bron_kerbosch<N, FN, CO>( |
| 64 | + connected: &mut FN, |
| 65 | + potential_clique: &HashSet<N>, |
| 66 | + remaining_nodes: &mut HashSet<N>, |
| 67 | + skip_nodes: &mut HashSet<N>, |
| 68 | + consumer: &mut CO, |
| 69 | +) where |
| 70 | + N: Eq + Hash + Clone, |
| 71 | + FN: FnMut(&N, &N) -> bool, |
| 72 | + CO: FnMut(&HashSet<N>), |
| 73 | +{ |
| 74 | + if remaining_nodes.is_empty() && skip_nodes.is_empty() { |
| 75 | + consumer(potential_clique); |
| 76 | + return; |
| 77 | + } |
| 78 | + let nodes_to_check = remaining_nodes.clone(); |
| 79 | + for node in &nodes_to_check { |
| 80 | + let mut new_potential_clique = potential_clique.clone(); |
| 81 | + new_potential_clique.insert(node.to_owned()); |
| 82 | + |
| 83 | + let mut new_remaining_nodes: HashSet<N> = remaining_nodes |
| 84 | + .iter() |
| 85 | + .filter(|n| *n != node && connected(node, n)) |
| 86 | + .cloned() |
| 87 | + .collect(); |
| 88 | + |
| 89 | + let mut new_skip_list: HashSet<N> = skip_nodes |
| 90 | + .iter() |
| 91 | + .filter(|n| *n != node && connected(node, n)) |
| 92 | + .cloned() |
| 93 | + .collect(); |
| 94 | + bron_kerbosch( |
| 95 | + connected, |
| 96 | + &new_potential_clique, |
| 97 | + &mut new_remaining_nodes, |
| 98 | + &mut new_skip_list, |
| 99 | + consumer, |
| 100 | + ); |
| 101 | + |
| 102 | + // We're done considering this node. If there was a way to form a clique with it, we |
| 103 | + // already discovered its maximal clique in the recursive call above. So, go ahead |
| 104 | + // and remove it from the list of remaining nodes and add it to the skip list. |
| 105 | + remaining_nodes.remove(node); |
| 106 | + skip_nodes.insert(node.to_owned()); |
| 107 | + } |
| 108 | +} |
0 commit comments