ExamplesBy LevelBy TopicLearning Paths
841 Advanced

Backtracking Framework

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Backtracking Framework" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. Many combinatorial optimization and enumeration problems — N-queens, Sudoku solving, permutation generation, subset enumeration, graph coloring, constraint satisfaction — require exploring a search space that grows exponentially with input size. Key difference from OCaml: | Aspect | Rust | OCaml |

Tutorial

The Problem

Many combinatorial optimization and enumeration problems — N-queens, Sudoku solving, permutation generation, subset enumeration, graph coloring, constraint satisfaction — require exploring a search space that grows exponentially with input size. Backtracking systematically explores candidates and abandons (backtracks from) any partial solution as soon as it determines it cannot lead to a valid complete solution. This pruning makes backtracking vastly more efficient than brute force for well-constrained problems, though still exponential in the worst case. Understanding backtracking as a reusable framework — not just specific puzzles — enables applying it to new combinatorial problems.

🎯 Learning Outcomes

  • • Implement the backtracking template: choose → explore → unchoose
  • • Use a used flag array to avoid revisiting elements in permutation generation
  • • Recognize constraint-based pruning: skip choices that violate constraints early
  • • Apply to N-queens: test placement validity before recursing, backtrack on conflict
  • • Understand the difference between backtracking (exact, complete) and heuristic search (approximate)
  • Code Example

    #![allow(clippy::all)]
    //! # Backtracking Framework
    pub fn generate_permutations<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
        let mut result = vec![];
        let mut current = vec![];
        let mut used = vec![false; items.len()];
        fn backtrack<T: Clone>(
            items: &[T],
            curr: &mut Vec<T>,
            used: &mut [bool],
            res: &mut Vec<Vec<T>>,
        ) {
            if curr.len() == items.len() {
                res.push(curr.clone());
                return;
            }
            for i in 0..items.len() {
                if !used[i] {
                    used[i] = true;
                    curr.push(items[i].clone());
                    backtrack(items, curr, used, res);
                    curr.pop();
                    used[i] = false;
                }
            }
        }
        backtrack(items, &mut current, &mut used, &mut result);
        result
    }
    
    pub fn generate_subsets<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
        let mut result = vec![];
        let mut current = vec![];
        fn backtrack<T: Clone>(items: &[T], start: usize, curr: &mut Vec<T>, res: &mut Vec<Vec<T>>) {
            res.push(curr.clone());
            for i in start..items.len() {
                curr.push(items[i].clone());
                backtrack(items, i + 1, curr, res);
                curr.pop();
            }
        }
        backtrack(items, 0, &mut current, &mut result);
        result
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_perms() {
            assert_eq!(generate_permutations(&[1, 2, 3]).len(), 6);
        }
        #[test]
        fn test_subsets() {
            assert_eq!(generate_subsets(&[1, 2, 3]).len(), 8);
        }
    }

    Key Differences

    AspectRustOCaml
    Used flagsVec<bool>bool array
    Result accumulation&mut Vec<Vec<T>>acc ref or continuation
    Choose/unchoosepush/pop on VecArray.set true/false
    Generic type<T: Clone>Parametric 'a
    N-queens variantSeparate fn queensSeparate or same framework
    Iterative variantIterator via generatorSequence with Seq.t

    OCaml Approach

    OCaml backtracking uses a used array as bool array mutated in-place. The recursive function returns unit and accumulates results via an acc ref. OCaml's functional style can also express this with continuation-passing: let rec backtrack cont current used = .... The Array.make n false initializes the used flags. OCaml's pattern matching for N-queens constraint checking reads naturally. The List.rev current at leaf nodes (if building the list from head prepending) recovers the correct order.

    Full Source

    #![allow(clippy::all)]
    //! # Backtracking Framework
    pub fn generate_permutations<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
        let mut result = vec![];
        let mut current = vec![];
        let mut used = vec![false; items.len()];
        fn backtrack<T: Clone>(
            items: &[T],
            curr: &mut Vec<T>,
            used: &mut [bool],
            res: &mut Vec<Vec<T>>,
        ) {
            if curr.len() == items.len() {
                res.push(curr.clone());
                return;
            }
            for i in 0..items.len() {
                if !used[i] {
                    used[i] = true;
                    curr.push(items[i].clone());
                    backtrack(items, curr, used, res);
                    curr.pop();
                    used[i] = false;
                }
            }
        }
        backtrack(items, &mut current, &mut used, &mut result);
        result
    }
    
    pub fn generate_subsets<T: Clone>(items: &[T]) -> Vec<Vec<T>> {
        let mut result = vec![];
        let mut current = vec![];
        fn backtrack<T: Clone>(items: &[T], start: usize, curr: &mut Vec<T>, res: &mut Vec<Vec<T>>) {
            res.push(curr.clone());
            for i in start..items.len() {
                curr.push(items[i].clone());
                backtrack(items, i + 1, curr, res);
                curr.pop();
            }
        }
        backtrack(items, 0, &mut current, &mut result);
        result
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_perms() {
            assert_eq!(generate_permutations(&[1, 2, 3]).len(), 6);
        }
        #[test]
        fn test_subsets() {
            assert_eq!(generate_subsets(&[1, 2, 3]).len(), 8);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_perms() {
            assert_eq!(generate_permutations(&[1, 2, 3]).len(), 6);
        }
        #[test]
        fn test_subsets() {
            assert_eq!(generate_subsets(&[1, 2, 3]).len(), 8);
        }
    }

    Deep Comparison

    OCaml vs Rust: Backtracking Framework

    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

  • Implement N-queens backtracking using this framework: add a validity check before recursing.
  • Implement Sudoku solver using backtracking with forward checking (propagate constraints after each placement).
  • Add an Iterator interface to the backtracking framework so permutations can be consumed lazily.
  • Implement combination generation (choose k from n) and verify the count equals C(n, k).
  • Apply backtracking to graph coloring: find a k-coloring of a graph or report that none exists.
  • Open Source Repos