petgraph/algo/
bridges.rs

1use crate::visit::{
2    EdgeRef, IntoEdgeReferences, IntoNeighbors, IntoNodeIdentifiers, NodeIndexable,
3};
4
5use alloc::{vec, vec::Vec};
6
7/// Find all [bridges](https://en.wikipedia.org/wiki/Bridge_(graph_theory)) in a simple undirected graph.
8///
9/// # Arguments
10/// * `graph`: a simple undirected graph.
11///
12/// # Returns
13/// * `impl Iterator`:  the iterator of edge references `G::EdgeRef` representing the edges
14///   of the input graph that are bridges. The order of the edges is arbitrary.
15///
16/// # Complexity
17/// * Time complexity: **O(|V| + |E|)**.
18/// * Auxiliary space: **O(|V|)**.
19///
20/// where **|V|** is the number of nodes and **|E|** is the number of edges.
21///
22///
23/// # Examples
24///
25/// ```
26/// use petgraph::algo::bridges::bridges;
27/// use petgraph::graph::UnGraph;
28/// use petgraph::visit::EdgeRef;
29///
30/// // Create the following graph:
31/// // 0----1    4
32/// //      | __/|
33/// // 5----2/---3
34///
35/// let mut g = UnGraph::new_undirected();
36/// let n0 = g.add_node(());
37/// let n1 = g.add_node(());
38/// let n2 = g.add_node(());
39/// let n3 = g.add_node(());
40/// let n4 = g.add_node(());
41/// let n5 = g.add_node(());
42/// let e0 = g.add_edge(n0, n1, ());
43/// let e1 = g.add_edge(n1, n2, ());
44/// let e2 = g.add_edge(n2, n3, ());
45/// let e3 = g.add_edge(n3, n4, ());
46/// let e4 = g.add_edge(n2, n4, ());
47/// let e5 = g.add_edge(n5, n2, ());
48///
49/// let bridges: Vec<_> = bridges(&g).map(|edge_ref| edge_ref.id()).collect();
50///
51/// // The bridges in this graph are the undirected edges {2, 5}, {1, 2}, {0, 1}.
52/// assert_eq!(bridges, vec![e0, e1, e5]);
53/// ```
54pub fn bridges<G>(graph: G) -> impl Iterator<Item = G::EdgeRef>
55where
56    G: IntoNodeIdentifiers + IntoNeighbors + NodeIndexable + IntoEdgeReferences,
57{
58    let mut clock: usize = 0usize;
59    // If and when a node was visited by the dfs
60    let mut visit_time = vec![None; graph.node_bound()];
61    // Lowest time on a node that is the target of a back-edge from the subtree rooted
62    // at the indexed node.
63    let mut earliest_backedge = vec![usize::MAX; graph.node_bound()];
64
65    for start in 0..graph.node_bound() {
66        // If node hasn't been visited yet, make it the root of a new dfs-tree in the forest.
67        if visit_time[start].is_none() {
68            visit_time[start] = Some(clock);
69            clock += 1;
70
71            // Perform a DFS starting at start
72            let start = graph.from_index(start);
73            let mut stack: Vec<(G::NodeId, G::Neighbors)> = vec![(start, graph.neighbors(start))];
74
75            while let Some((stack_frame, rest_of_stack)) = stack.split_last_mut() {
76                let &mut (node, ref mut neighbors) = stack_frame;
77                let parent = rest_of_stack.last().map(|&(n, _)| n);
78
79                let node_index = graph.to_index(node);
80
81                if let Some(child) = neighbors.next() {
82                    // Pre-order DFS
83                    if parent != Some(child) {
84                        let child_index = graph.to_index(child);
85
86                        if let Some(time) = visit_time[child_index] {
87                            earliest_backedge[node_index] = earliest_backedge[node_index].min(time);
88                        } else {
89                            visit_time[child_index] = Some(clock);
90                            clock += 1;
91                            stack.push((child, graph.neighbors(child)));
92                        }
93                    }
94                } else {
95                    // Post-order DFS
96                    if let Some(parent) = parent {
97                        let parent_index = graph.to_index(parent);
98                        earliest_backedge[parent_index] =
99                            earliest_backedge[parent_index].min(earliest_backedge[node_index]);
100                    }
101                    stack.pop();
102                }
103            }
104        }
105    }
106
107    graph.edge_references().filter(move |edge| {
108        let source_index = graph.to_index(edge.source());
109        let target_index = graph.to_index(edge.target());
110
111        // All nodes have been visited by the time we return, so unwraps are safe.
112        // The node with the lower visit time is the "parent" in the dfs-forest created above.
113        let (parent, node) =
114            if visit_time[source_index].unwrap() < visit_time[target_index].unwrap() {
115                (source_index, target_index)
116            } else {
117                (target_index, source_index)
118            };
119
120        // If there's no back-edge to before parent, then this the only way from parent to here
121        // is directly from parent, so it's a bridge edge.
122        earliest_backedge[node] > visit_time[parent].unwrap()
123    })
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::graph::EdgeReference;
130    use crate::graph::UnGraph;
131    use crate::visit::EdgeRef;
132
133    #[test]
134    fn test_bridges() {
135        let mut g = UnGraph::<i8, i8>::new_undirected();
136        let bridge_nodes = |g: &_| {
137            bridges(g)
138                .map(|e: EdgeReference<_>| (e.source(), e.target()))
139                .collect::<Vec<_>>()
140        };
141
142        assert_eq!(bridge_nodes(&g), vec![]);
143        let n0 = g.add_node(0);
144        assert_eq!(bridge_nodes(&g), vec![]);
145        let n1 = g.add_node(1);
146        assert_eq!(bridge_nodes(&g), vec![]);
147        g.add_edge(n0, n1, 0);
148        assert_eq!(bridge_nodes(&g), vec![(n0, n1)]);
149        let n2 = g.add_node(2);
150        assert_eq!(bridge_nodes(&g), vec![(n0, n1)]);
151        g.add_edge(n2, n1, 1);
152        assert_eq!(bridge_nodes(&g), vec![(n0, n1), (n2, n1)]);
153        g.add_edge(n0, n2, 2);
154        assert_eq!(bridge_nodes(&g), vec![]);
155        let n3 = g.add_node(3);
156        let n4 = g.add_node(4);
157        g.add_edge(n2, n3, 3);
158        g.add_edge(n3, n4, 4);
159        assert_eq!(bridge_nodes(&g), vec![(n2, n3), (n3, n4)]);
160        g.add_edge(n3, n0, 5);
161        assert_eq!(bridge_nodes(&g), vec![(n3, n4)]);
162    }
163}