Coverage Report

Created: 2026-01-25 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/petgraph-0.8.3/src/algo/bridges.rs
Line
Count
Source
1
use crate::visit::{
2
    EdgeRef, IntoEdgeReferences, IntoNeighbors, IntoNodeIdentifiers, NodeIndexable,
3
};
4
5
use 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
/// ```
54
0
pub fn bridges<G>(graph: G) -> impl Iterator<Item = G::EdgeRef>
55
0
where
56
0
    G: IntoNodeIdentifiers + IntoNeighbors + NodeIndexable + IntoEdgeReferences,
57
{
58
0
    let mut clock: usize = 0usize;
59
    // If and when a node was visited by the dfs
60
0
    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
0
    let mut earliest_backedge = vec![usize::MAX; graph.node_bound()];
64
65
0
    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
0
        if visit_time[start].is_none() {
68
0
            visit_time[start] = Some(clock);
69
0
            clock += 1;
70
71
            // Perform a DFS starting at start
72
0
            let start = graph.from_index(start);
73
0
            let mut stack: Vec<(G::NodeId, G::Neighbors)> = vec![(start, graph.neighbors(start))];
74
75
0
            while let Some((stack_frame, rest_of_stack)) = stack.split_last_mut() {
76
0
                let &mut (node, ref mut neighbors) = stack_frame;
77
0
                let parent = rest_of_stack.last().map(|&(n, _)| n);
78
79
0
                let node_index = graph.to_index(node);
80
81
0
                if let Some(child) = neighbors.next() {
82
                    // Pre-order DFS
83
0
                    if parent != Some(child) {
84
0
                        let child_index = graph.to_index(child);
85
86
0
                        if let Some(time) = visit_time[child_index] {
87
0
                            earliest_backedge[node_index] = earliest_backedge[node_index].min(time);
88
0
                        } else {
89
0
                            visit_time[child_index] = Some(clock);
90
0
                            clock += 1;
91
0
                            stack.push((child, graph.neighbors(child)));
92
0
                        }
93
0
                    }
94
                } else {
95
                    // Post-order DFS
96
0
                    if let Some(parent) = parent {
97
0
                        let parent_index = graph.to_index(parent);
98
0
                        earliest_backedge[parent_index] =
99
0
                            earliest_backedge[parent_index].min(earliest_backedge[node_index]);
100
0
                    }
101
0
                    stack.pop();
102
                }
103
            }
104
0
        }
105
    }
106
107
0
    graph.edge_references().filter(move |edge| {
108
0
        let source_index = graph.to_index(edge.source());
109
0
        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
0
        let (parent, node) =
114
0
            if visit_time[source_index].unwrap() < visit_time[target_index].unwrap() {
115
0
                (source_index, target_index)
116
            } else {
117
0
                (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
0
        earliest_backedge[node] > visit_time[parent].unwrap()
123
0
    })
124
0
}
125
126
#[cfg(test)]
127
mod 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
}