ExamplesBy LevelBy TopicLearning Paths
037 Intermediate

037 — Preorder Traversal Sequence

Functional Programming

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

  • • Implement preorder traversal: visit root, then left subtree, then right subtree
  • • Use a dot-string encoding where . represents a leaf (self-delimiting format)
  • • Understand why the dot-string encoding is self-delimiting (unlike the format in #036)
  • • Reconstruct a tree from its preorder dot-string sequence
  • • Contrast preorder with inorder (root between children) and postorder (root after)
  • • Collect values in pre-order (root, then left subtree, then right subtree) — order used for tree serialization
  • • For large trees, use an explicit Vec stack instead of recursion to avoid stack overflow
  • Code Example

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Key Differences

  • 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.
  • Self-delimiting encoding: The dot-string encoding is self-delimiting because . consumes exactly one position, and a node character consumes one position followed by two self-delimiting subtrees. No length prefix needed.
  • Reconstruction state: Rust passes &mut usize cursor or slices. OCaml's functional version returns (result, remaining_string) pairs — the monadic parser style.
  • Round-trip uniqueness: Preorder dot-string uniquely determines the tree. This is used for equality testing: two trees are equal iff their preorder dot-strings are equal.
  • Traversal order: Pre-order visits root → left → right. This produces the tree in "polish notation" order — useful for serialization, expression evaluation (prefix notation), and copying trees.
  • Stack-based vs recursive: The recursive version is straightforward but O(d) stack depth. The explicit-stack version maintains a 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.
  • Stack-based iterative: Using a 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 conversion

    Deep Comparison

    OCaml vs Rust: Tree Preorder

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Postorder: Write 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.
  • Tree from pre+in: Given a preorder sequence and an inorder sequence (both without dots), reconstruct the unique binary tree. This is the classic interview question.
  • Preorder vs BFS: Compare the preorder sequence with the BFS level-order sequence on the same tree. Draw the tree, list both sequences, and explain the structural difference.
  • Iterative preorder: Implement preorder_iterative<T: Clone>(tree: &Tree<T>) -> Vec<T> using an explicit Vec stack to avoid recursion — safe for arbitrarily deep trees.
  • Serialize via preorder: Show how preorder traversal together with the None/Some distinction uniquely serializes a binary tree. Implement both serialize (preorder with None for leaves) and deserialize (consume from iterator).
  • Open Source Repos