graph_traversal/
bipartite.rs

1use graph_collections::Queue;
2use graph_core::{Graph, NodeId};
3use std::collections::HashMap;
4
5/// Result of a bipartite check: the two colour partitions.
6///
7/// `left` and `right` together contain every reachable node from the seed
8/// used in [`is_bipartite`].
9pub struct BipartitePartitions {
10    /// Nodes coloured `0`.
11    pub left: Vec<NodeId>,
12    /// Nodes coloured `1`.
13    pub right: Vec<NodeId>,
14}
15
16/// Checks whether the graph is **bipartite** and returns its 2-colouring.
17///
18/// A graph is bipartite iff its nodes can be split into two sets such that
19/// every edge crosses between the sets (no edge is within the same set).
20/// Equivalently, a graph is bipartite iff it contains no odd-length cycle.
21///
22/// Uses BFS with two-colouring. Returns `Some(partitions)` if bipartite,
23/// or `None` if a conflict (odd cycle) is found.
24///
25/// # Examples
26///
27/// ```
28/// use graph_core::{AdjacencyList, Graph};
29/// use graph_traversal::is_bipartite;
30///
31/// // Even cycle (square) is bipartite.
32/// let mut g: AdjacencyList<()> = AdjacencyList::undirected();
33/// let a = g.add_node(());
34/// let b = g.add_node(());
35/// let c = g.add_node(());
36/// let d = g.add_node(());
37/// g.add_edge(a, b, 1.0).unwrap();
38/// g.add_edge(b, c, 1.0).unwrap();
39/// g.add_edge(c, d, 1.0).unwrap();
40/// g.add_edge(d, a, 1.0).unwrap();
41/// assert!(is_bipartite(&g).is_some());
42///
43/// // Odd cycle (triangle) is NOT bipartite.
44/// let mut tri: AdjacencyList<()> = AdjacencyList::undirected();
45/// let a = tri.add_node(());
46/// let b = tri.add_node(());
47/// let c = tri.add_node(());
48/// tri.add_edge(a, b, 1.0).unwrap();
49/// tri.add_edge(b, c, 1.0).unwrap();
50/// tri.add_edge(c, a, 1.0).unwrap();
51/// assert!(is_bipartite(&tri).is_none());
52/// ```
53pub fn is_bipartite<G: Graph>(graph: &G) -> Option<BipartitePartitions> {
54    // colour map: 0 or 1 per node.
55    let mut colour: HashMap<NodeId, u8> = HashMap::new();
56    let mut queue: Queue<NodeId> = Queue::new();
57
58    for seed in graph.nodes() {
59        if colour.contains_key(&seed) {
60            continue;
61        }
62
63        colour.insert(seed, 0);
64        queue.enqueue(seed);
65
66        while let Some(node) = queue.dequeue() {
67            let node_colour = colour[&node];
68            for (neighbour, _) in graph.neighbors(node) {
69                match colour.get(&neighbour) {
70                    Some(&c) if c == node_colour => return None, // conflict
71                    None => {
72                        colour.insert(neighbour, 1 - node_colour);
73                        queue.enqueue(neighbour);
74                    }
75                    _ => {}
76                }
77            }
78        }
79    }
80
81    let mut left = Vec::new();
82    let mut right = Vec::new();
83    for (node, c) in &colour {
84        if *c == 0 {
85            left.push(*node);
86        } else {
87            right.push(*node);
88        }
89    }
90
91    Some(BipartitePartitions { left, right })
92}