033 — Collect the Nodes at a Given Level
Tutorial Video
Text description (accessibility)
This video demonstrates the "033 — Collect the Nodes at a Given Level" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Collecting all nodes at a specific depth (OCaml 99 Problems #33) — where the root is at level 1 — is a level-order query that appears in breadth-first search, layer-by-layer neural network processing, and tree visualization. Key difference from OCaml: 1. **Level vs depth**: The problem uses 1
Tutorial
The Problem
Collecting all nodes at a specific depth (OCaml 99 Problems #33) — where the root is at level 1 — is a level-order query that appears in breadth-first search, layer-by-layer neural network processing, and tree visualization. It is also the key to level-order traversal (BFS on trees).
Level queries are the bridge between depth-first structural recursion (which processes all of a subtree before moving to the next) and breadth-first level-by-level processing. Collecting all nodes at a given level can be done either with DFS (pass the target level as a decreasing counter) or with BFS (queue-based).
🎯 Learning Outcomes
level - 1 at each Node, collect when level == 1at_level(tree, level-1) on each child collects nodes one level deeperCode Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
when level = 1 guard in the match arm. Rust: if level == 1 { return ... } or a separate match arm with a guard k if k == 1.@ efficiency**: OCaml's at_level l (level-1) @ at_level r (level-1) copies the left result. Rust's extend approach is more memory-efficient.at_level for each level (O(n·d) total).Vec concatenation:** result.extend(at_level(left, n-1)) is O(k) where k is the number of nodes found. For large trees at deep levels, this allocates frequently — accumulator style is faster.&mut Vec<T> as an accumulator parameter avoids O(k) extend overhead per recursive call on deeply nested trees.OCaml Approach
OCaml's version: let rec at_level tree level = match tree with | Leaf -> [] | Node (x, _, _) when level = 1 -> [x] | Node (_, l, r) -> at_level l (level - 1) @ at_level r (level - 1). The when level = 1 guard returns the value. Otherwise it recurses deeper with decremented level.
Full Source
#![allow(clippy::all)]
// Placeholder — pending conversionDeep Comparison
OCaml vs Rust: At Level
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
level_order<T: Clone>(tree: &Tree<T>) -> Vec<Vec<T>> that returns [nodes_at_level_1, nodes_at_level_2, ...] using a queue-based BFS.max_sum_level(tree: &Tree<i32>) -> usize that returns the level with the highest sum of node values.zigzag<T: Clone>(tree: &Tree<T>) -> Vec<Vec<T>> like level-order but alternating left-to-right and right-to-left on each level (a common interview problem).level_order<T: Clone>(tree: &Tree<T>) -> Vec<Vec<T>> returning all levels as a list of lists, using BFS with a queue.width(tree: &Tree<T>) -> usize returning the maximum number of nodes at any single level — the "width" of the tree.