037 — Preorder Traversal Sequence
Tutorial Video
Text description (accessibility)
This video demonstrates the "037 — Preorder Traversal Sequence" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Preorder traversal visits nodes in root-left-right order, producing a sequence where the root always comes before its descendants. Key difference from OCaml: 1. **String construction**: Rust uses `format!` or `String::push`. OCaml uses `^` (string concatenation) which is O(n) per call — avoid in inner loops; use `Buffer` instead.
Tutorial
The Problem
Preorder traversal visits nodes in root-left-right order, producing a sequence where the root always comes before its descendants. This ordering has a critical property: in a full binary tree (where leaves are uniquely identifiable), the preorder sequence uniquely determines the tree. Combined with the inorder sequence (from example 038), any binary tree can be reconstructed.
Preorder traversal underlies expression tree serialization (prefix notation: "+ 3 4" rather than "3 + 4"), directory tree listing (find command), syntax tree serialization in compilers, and tree copying algorithms. Depth-first search (DFS) on graphs is a generalization of preorder traversal.
🎯 Learning Outcomes
. represents a leaf (self-delimiting format)Vec stack instead of recursion to avoid stack overflowCode Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
format! or String::push. OCaml uses ^ (string concatenation) which is O(n) per call — avoid in inner loops; use Buffer instead.. consumes exactly one position, and a node character consumes one position followed by two self-delimiting subtrees. No length prefix needed.&mut usize cursor or slices. OCaml's functional version returns (result, remaining_string) pairs — the monadic parser style.Vec<&Tree<T>> and processes nodes iteratively — safe for deep trees in Rust (no TCO guarantee).extend for subtrees:** result.extend(preorder(left)); result.extend(preorder(right)) copies values. An accumulator approach (preorder_aux(tree, &mut acc)) is more efficient for large trees.Vec<&Tree<T>> stack avoids deep recursion. Push right child first, then left, so left is processed first (LIFO stack reverses order).OCaml Approach
OCaml's version: let rec preorder = function | Leaf -> "." | Node (c, l, r) -> String.make 1 c ^ preorder l ^ preorder r. Reconstruction: let rec of_preorder s = match s.[0] with | '.' -> (Leaf, String.sub s 1 ...) | c -> let (l, s') = of_preorder (String.sub s 1 ...) in let (r, s'') = of_preorder s' in (Node (c, l, r), s''). The function returns (tree, remaining_string).
Full Source
#![allow(clippy::all)]
// Placeholder — pending conversionDeep Comparison
OCaml vs Rust: Tree Preorder
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
postorder(tree: &Tree<char>) -> String with left-right-root order using a similar dot-string encoding. The dot for leaves must appear after the two empty subtrees.preorder_iterative<T: Clone>(tree: &Tree<T>) -> Vec<T> using an explicit Vec stack to avoid recursion — safe for arbitrarily deep trees.None/Some distinction uniquely serializes a binary tree. Implement both serialize (preorder with None for leaves) and deserialize (consume from iterator).