Rust Program to Implement Breadth-First Search

Rust Program to Implement Breadth-First Search

Breadth-First Search, often known as BFS, is one of the most important graph traversal algorithms used in computer science. It works by exploring all neighbors at the current level before moving deeper, making the search feel like it spreads outward in waves from the starting node. This level-by-level approach is very helpful when you need the shortest path in an unweighted graph, or when you want to explore everything close to you before stepping farther away.

Pluralsight Logo
Accelerate your tech career
with hands-on learning.
Whether you're a tech newbie or a total pro,
get the skills and confidence to land your next move.
Start 10-Day Free Trial

Because of how predictable and organized BFS is, it appears in real-world systems such as social networks, routing algorithms, web crawlers, AI pathfinding, and network broadcasting. In Rust, implementing BFS gives beginners a chance to learn about queues, safe memory handling, vectors, and the beauty of structured traversal. Rust’s strong guarantees make BFS fast, reliable, and safe even when the graph becomes large. This makes BFS a perfect starting point for learners who want to master searching algorithms in Rust.

Program 1: Basic BFS Using a Queue

This first program shows the simplest version of BFS using an adjacency list to represent the graph. It uses a queue to explore nodes in layers, which is the core idea of breadth-first traversal.

use std::collections::{HashSet, VecDeque};

fn bfs(start: usize, graph: &Vec<Vec<usize>>) {

    let mut visited = HashSet::new();
    let mut queue = VecDeque::new();

    visited.insert(start);
    queue.push_back(start);

    while let Some(node) = queue.pop_front() {

        println!("Visited: {}", node);

        for &next in &graph[node] {

            if !visited.contains(&next) {
                visited.insert(next);
                queue.push_back(next);
            }

        }

    }

}

fn main() {

    let graph = vec![
        vec![1, 2],
        vec![0, 3],
        vec![0, 4],
        vec![1],
        vec![1],
    ];

    bfs(0, &graph);

}

This program starts from a chosen node and explores everything connected to it in order of distance. The queue makes sure that nodes are visited in the correct layer, giving BFS its predictable and structured behavior. Beginners will find this helpful because it clearly separates immediate neighbors from more distant ones, making the search easy to follow.

Program 2: BFS for Shortest Path in an Unweighted Graph

This program builds on the previous one but adds a simple way to track the shortest path using a parent list. BFS naturally finds the shortest path in unweighted graphs because it explores level by level.

use std::collections::{HashSet, VecDeque};

fn shortest_path(start: usize, target: usize, graph: &Vec<Vec<usize>>) -> Vec<usize> {

    let mut visited = HashSet::new();
    let mut queue = VecDeque::new();
    let mut parent = vec![None; graph.len()];

    visited.insert(start);
    queue.push_back(start);

    while let Some(node) = queue.pop_front() {

        if node == target {
            break;
        }

        for &next in &graph[node] {

            if !visited.contains(&next) {
                visited.insert(next);
                parent[next] = Some(node);
                queue.push_back(next);
            }

        }

    }

    let mut path = Vec::new();
    let mut current = target;

    while let Some(p) = parent[current] {
        path.push(current);
        current = p;
    }

    path.push(start);
    path.reverse();

    path

}

fn main() {

    let graph = vec![
        vec![1, 2],
        vec![0, 3, 4],
        vec![0],
        vec![1],
        vec![1],
    ];

    let path = shortest_path(0, 4, &graph);

    println!("Shortest path: {:?}", path);

}

Here, BFS records how each node was reached by storing its parent. Once the target is found, the program reconstructs the path by walking backward through these parent links. This style helps beginners understand why BFS is perfect for shortest paths when all edges have equal weight.

Program 3: BFS on a Weighted Graph (Ignoring Weights)

Even when edges have weights, BFS can still be used if the weights do not matter. This version simply prints the weights as it travels.

use std::collections::{HashSet, VecDeque};

fn bfs_weighted(start: usize, graph: &Vec<Vec<(usize, i32)>>) {

    let mut visited = HashSet::new();
    let mut queue = VecDeque::new();

    visited.insert(start);
    queue.push_back(start);

    while let Some(node) = queue.pop_front() {

        println!("At node: {}", node);

        for &(next, weight) in &graph[node] {

            println!("Edge to {} with weight {}", next, weight);

            if !visited.contains(&next) {
                visited.insert(next);
                queue.push_back(next);
            }

        }

    }

}

fn main() {

    let graph = vec![
        vec![(1, 5), (2, 3)],
        vec![(0, 5)],
        vec![(0, 3)],
    ];

    bfs_weighted(0, &graph);

}

In this example, BFS explores the graph in the same structured layers as before, even though weights are present. This is useful when weights are informational rather than functional. It helps beginners see that BFS depends on structure, not weight, making it flexible across many graph types.

Program 4: BFS to Detect Connected Components

This program shows how BFS can help explore all separate groups inside a graph. Each time BFS starts from an unvisited node, it discovers one full component.

use std::collections::{HashSet, VecDeque};

fn explore(start: usize, graph: &Vec<Vec<usize>>, visited: &mut HashSet<usize>) {

    let mut queue = VecDeque::new();
    visited.insert(start);
    queue.push_back(start);

    while let Some(node) = queue.pop_front() {

        for &next in &graph[node] {

            if !visited.contains(&next) {
                visited.insert(next);
                queue.push_back(next);
            }

        }

    }

}

fn main() {

    let graph = vec![
        vec![1],
        vec![0],
        vec![3],
        vec![2],
    ];

    let mut visited = HashSet::new();
    let mut count = 0;

    for i in 0..graph.len() {

        if !visited.contains(&i) {

            count += 1;

            println!("Component {}", count);
            explore(i, &graph, &mut visited);

        }

    }

    println!("Total connected components: {}", count);

}

This code helps beginners see how BFS can divide a graph into meaningful clusters. Each run of BFS explores one whole component, making it a helpful tool for grouping and classification tasks.

Program 5: BFS for Level Order Traversal of a Tree

Trees are natural structures for BFS because each level represents clear layers of nodes. This program shows how BFS moves through a tree from top to bottom.

use std::collections::VecDeque;

#[derive(Debug)]
struct Node {
    value: i32,
    children: Vec<usize>,
}

fn bfs_tree(start: usize, tree: &Vec<Node>) {

    let mut queue = VecDeque::new();
    queue.push_back(start);

    while let Some(idx) = queue.pop_front() {

        let node = &tree[idx];

        println!("Node value: {}", node.value);

        for &child in &node.children {
            queue.push_back(child);
        }

    }

}

fn main() {

    let tree = vec![
        Node { value: 10, children: vec![1, 2] },
        Node { value: 20, children: vec![] },
        Node { value: 30, children: vec![3] },
        Node { value: 40, children: vec![] },
    ];

    bfs_tree(0, &tree);

}

The program follows a clean sequence of nodes, level by level, showing how BFS works on tree structures without needing a visited set. Beginners can clearly see the flow of traversal from parent to children, which makes BFS on trees one of the easiest forms of graph exploration.

Frequently Asked Questions (FAQ)

This section answers common questions that beginners often ask while learning BFS in Rust. The aim is to make each idea easy to understand so you can feel more confident when working with graphs and trees.

Q1: Why does BFS use a queue instead of recursion?
BFS explores a graph level by level, so it needs a structure that keeps items in the order they should be processed. A queue is perfect because it handles elements in a first-come, first-served manner. Recursion goes deep first, which does not match the level-based nature of BFS.

Q2: Does BFS always return the shortest path?
Yes, BFS finds the shortest path in an unweighted graph. It moves outward one layer at a time, so the first time it reaches a node, that path is guaranteed to be the shortest. This makes BFS a very popular choice for problems involving distances.

Q3: Is BFS faster than DFS?
Both BFS and DFS have the same time complexity in most cases, which is O(V + E). The difference lies in how they explore. BFS is better when the shortest path matters, while DFS is more useful for deep exploration or solving problems like topological sorting.

Q4: Can BFS get stuck in loops without a visited set?
Yes, BFS can run forever if you do not track what you have already visited. Graphs often contain cycles, and without marking visited nodes, the queue keeps filling up with the same places again and again. A visited set ensures BFS stays safe and efficient.

Q5: Does BFS work only on graphs or also on trees?
BFS works well on both trees and general graphs. On trees, it simply moves level by level from the root. On graphs, it does the same but relies on the visited set to handle cycles and prevent repeated work.

Conclusion

Breadth-First Search is one of the most useful algorithms every Rust learner should understand. Its level-based exploration makes it predictable, simple, and perfect for shortest paths, tree traversals, and discovering connected components. Rust makes the whole process safe and efficient, so beginners can focus on learning the logic without worrying about memory errors. As you continue exploring Rust, working with BFS will prepare you for more advanced algorithms such as Dijkstra, A*, and more complex graph structures. Keep practicing, try building your own graph examples, and enjoy the journey of learning algorithms the Rust way.

Scroll to Top