Introduction
Breadth First Search is a fundamental tree traversal algorithm that visits all nodes at the current depth before moving to nodes at the next depth level. In this tutorial, we’ll implement BFS in Rust using a binary tree structure. We’ll break down each component and explore how it works.
1. Setting Up the Tree Structure
use std::collections::VecDeque; #[derive(Debug)] struct Tree { data: u32, left: Option<Box<Tree>>, right: Option<Box<Tree>>, }
Let’s break this down:
- We import
VecDeque
from the standard library, which we’ll use as our queue data structure - #[derive(Debug)] allows us to print our tree structure for debugging
- The
Tree
struct represents a node in our binary tree with:data:
The value stored in the nodeleft
andright
: Optional boxed references to child nodes- We use
Option
because a node might not have children Box
is used for heap allocation, necessary for recursive structures in Rust
2. Implementing Tree Methods
impl Tree { fn new(data: u32) -> Self { Tree { data, left: None, right: None, } } fn insert_left(&mut self, data: u32) { self.left = Some(Box::new(Tree::new(data))); } fn insert_right(&mut self, data: u32) { self.right = Some(Box::new(Tree::new(data))); }
- new: A constructor that creates a new tree node with given data
- insert_left: Creates and inserts a new node as the left child
- insert_right: Creates and inserts a new node as the right child
3. The BFS Implementation
fn bfs(&self) -> Vec<u32> { let mut result = Vec::new(); let mut queue = VecDeque::new(); queue.push_back(self); while let Some(node) = queue.pop_front() { result.push(node.data); if let Some(left) = &node.left { queue.push_back(left); } if let Some(right) = &node.right { queue.push_back(right); } } result }
- Creates a vector to store the traversal result
- Initializes a queue with the root node
- Continues processing nodes until the queue is empty:
- Removes and processes the front node
- Adds its value to the result
- Queues up the left and right children (if they exist)
- Returns the completed traversal
4. Using the Implementation
fn main() { // Create the tree structure let mut root = Tree::new(1); // Build left subtree root.insert_left(2); if let Some(node) = &mut root.left { node.insert_left(4); node.insert_right(6); } // Build right subtree root.insert_right(3); if let Some(node) = &mut root.right { node.insert_right(5); } // Perform BFS and print results let bfs_result = root.bfs(); println!("BFS traversal: {:?}", bfs_result); }
1 / \ 2 3 / \ \ 4 6 5
When we run the BFS, it will visit nodes in this order: 1, 2, 3, 4, 6, 5
Time and Space Complexity
- Time Complexity: O(n) where n is the number of nodes
- Space Complexity: O(w) where w is the maximum width of the tree
This implementation will visit each node exactly once while keeping at most one level of the tree in the queue at any time.
Conclusion
This Rust implementation of BFS demonstrates how to create a memory-safe, efficient tree traversal algorithm while leveraging Rust’s powerful features like pattern matching and ownership system. The code is both safe and idiomatic, making it a good example of how to implement classical algorithms in Rust.