Function depth_first_search

Source
pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, visitor: F) -> C
where G: IntoEdges + Visitable, I: IntoIterator<Item = G::NodeId>, F: FnMut(DfsEvent<G::NodeId, &G::EdgeWeight>) -> C, C: ControlFlow,
Expand description

An iterative depth first search.

It is an iterative implementation of the upstream petgraph depth_first_search function.

Starting points are the nodes in the iterator starts (specify just one start vertex x by using Some(x)).

The traversal emits discovery and finish events for each reachable vertex, and edge classification of each reachable edge. visitor is called for each event, see petgraph::DfsEvent for possible values.

The return value should implement the trait ControlFlow, and can be used to change the control flow of the search.

Control Implements ControlFlow such that Control::Continue resumes the search. Control::Break will stop the visit early, returning the contained value. Control::Prune will stop traversing any additional edges from the current node and proceed immediately to the Finish event.

There are implementations of ControlFlow for (), and Result<C, E> where C: ControlFlow. The implementation for () will continue until finished. For Result, upon encountering an E it will break, otherwise acting the same as C.

*Panics if you attempt to prune a node from its Finish event.

§Example returning Control.

Find a path from vertex 0 to 5, and exit the visit as soon as we reach the goal vertex.

use rustworkx_core::petgraph::prelude::*;
use rustworkx_core::petgraph::graph::node_index as n;
use rustworkx_core::petgraph::visit::Control;

use rustworkx_core::traversal::{DfsEvent, depth_first_search};

let gr: Graph<(), ()> = Graph::from_edges(&[
    (0, 1), (0, 2), (0, 3),
    (1, 3),
    (2, 3), (2, 4),
    (4, 0), (4, 5),
]);

// record each predecessor, mapping node → node
let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
let start = n(0);
let goal = n(5);
depth_first_search(&gr, Some(start), |event| {
    if let DfsEvent::TreeEdge(u, v, _) = event {
        predecessor[v.index()] = u;
        if v == goal {
            return Control::Break(v);
        }
    }
    Control::Continue
});

let mut next = goal;
let mut path = vec![next];
while next != start {
    let pred = predecessor[next.index()];
    path.push(pred);
    next = pred;
}
path.reverse();
assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);

§Example returning a Result.

use rustworkx_core::petgraph::graph::node_index as n;
use rustworkx_core::petgraph::prelude::*;
use rustworkx_core::petgraph::visit::Time;

use rustworkx_core::traversal::{DfsEvent, depth_first_search};

let gr: Graph<(), ()> = Graph::from_edges(&[(0, 1), (1, 2), (1, 1), (2, 1)]);
let start = n(0);
let mut back_edges = 0;
let mut discover_time = 0;

#[derive(Debug)]
struct BackEdgeFound {
    source: NodeIndex,
    target: NodeIndex,
}

// Stop the search, the first time a BackEdge is encountered.
let result = depth_first_search(&gr, Some(start), |event| {
    match event {
        // In the cases where Ok(()) is returned,
        // Result falls back to the implementation of Control on the value ().
        // In the case of (), this is to always return Control::Continue.
        // continuing the search.
        DfsEvent::Discover(_, Time(t)) => {
            discover_time = t;
            Ok(())
        }
        DfsEvent::BackEdge(u, v, _) => {
            back_edges += 1;
            // the implementation of ControlFlow for Result,
            // treats this Err value as Continue::Break
            Err(BackEdgeFound {source: u, target: v})
        }
        _ => Ok(()),
    }
});

// Even though the graph has more than one cycle,
// The number of back_edges visited by the search should always be 1.
assert_eq!(back_edges, 1);
println!("discover time:{:?}", discover_time);
println!("number of backedges encountered: {}", back_edges);
println!("back edge: ({:?})", result.unwrap_err());