Search code examples
rustbinary-search-treerust-obsolete

difficulty with rust binary tree implementation


I am trying to implement a simple binary search tree in Rust but I am having difficulty pinning down an issue with inserting nodes. I am using the following data structures and functions.

enum BinaryTree<T> {
    Leaf(T),
    Branch(T, Box<BinaryTree<T>>, Box<BinaryTree<T>>),
    Null,
}

fn createBinarySearchTree(vector: Vec<int>) -> BinaryTree<int> {        
    fn insertNode(val: int, btree: &BinaryTree<int>) -> BinaryTree<int> {
        match btree {
            &Leaf(tval) if val > tval => Branch(tval, box Null, box Leaf(val)),     
            &Leaf(tval) if val < tval => Branch(tval, box Leaf(val), box Null),
            &Branch(tval, box ref left, box ref right) if val > tval => insertNode(val,right),
            &Branch(tval, box ref left, box ref right) if val < tval => insertNode(val,left),
            &Null => Leaf(val),
            &Leaf(lval) if val == lval => Leaf(val),
            &Branch(lval, box ref left, box ref right) if val == lval => fail!("already has a node with {}", lval),
            _ => Null,
        }
    }

    let mut tree = Null;
    for v in vector.iter() {
        tree = insertNode(*v, &tree);
    }

    let immuTree = tree;
    immuTree
}

fn printTree(tree: &BinaryTree<int>) {
    fn innerPrint(prefix: &str, tree: &BinaryTree<int>, level: int) {
        let lvDesc = format!("lv {}", level);
        match tree {
            &Leaf(val) => println!("{}-{} leaf: {}", lvDesc, prefix, val),
            &Branch(val, box ref left, box ref right) => {
                println!("{}-{} node: {}", lvDesc, prefix, val);
                innerPrint("left branch <-", left, level + 1);
                innerPrint("right branch ->", right, level + 1);
            },
            &Null => println!("end"),
        }
    }
    innerPrint("root", tree, 0);
}

Upon calling printTree(&createBinarySearchTree(vec![43,2,45,7,72,28,34,33])) the tree only prints out 33,34 and unfortunately I cannot debug as compiling with debug info causes a compiler error. Also I have tried to return a branch when I match on branch on inserting but this requires me to clone the leaf/give ownership in ways that I just can't wrap my head around yet. So any help would be much appreciated

Cheers


Solution

  • I believe these branches to be at fault:

    &Branch(tval, box ref left, box ref right) if val > tval => insertNode(val, right),
    &Branch(tval, box ref left, box ref right) if val < tval => insertNode(val, left),
    

    Since you are mutating the original tree, each of those branches looses the original tree root. Supposed fix (untested):

    &Branch(tval, box ref left, box ref right) if val > tval => Branch(tval, left, insertNode(val, right)),
    &Branch(tval, box ref left, box ref right) if val < tval => Branch(tval, insertNode(val, left), right),
    

    EDIT

    Well the idea was right, but you are correct that Rust complains about moving out of the & pointer behind a pattern guard, so I had to do another match inside (which turns out for the better). I also couldn't ignore the naming so I cleaned it up in accordance with Rust coding style:

    use std::fmt::Show;
    
    enum BinaryTree<T> {
        Leaf(T),
        Branch(T, Box<BinaryTree<T>>, Box<BinaryTree<T>>),
        Null,
    }
    
    fn create_binary_search_tree(vector: Vec<int>) -> BinaryTree<int> {
        fn insert_node<T: Copy + Ord + Show>(val: T, btree: BinaryTree<T>) -> BinaryTree<T> {
            match btree {
                Leaf(tval) if val > tval => Branch(tval, box Null, box Leaf(val)),   
                Leaf(tval) if val < tval => Branch(tval, box Leaf(val), box Null),
                Branch(tval, left, right) => match val.cmp(&tval) {
                    Greater => Branch(tval, left, box insert_node(val, *right)),
                    Less    => Branch(tval, box insert_node(val, *left), right),
                    Equal   => fail!("already has a node with {}", tval),
                },
                Null => Leaf(val),
                Leaf(lval) if val == lval => Leaf(val),
                _ => Null,
            }
        }
    
        let mut tree = Null;
        for v in vector.iter() {
            tree = insert_node(*v, tree);
        }
    
        let immuTree = tree;
        immuTree
    }
    
    
    fn print_tree(tree: &BinaryTree<int>) {
        fn inner_print(prefix: &str, tree: &BinaryTree<int>, level: int) {
            let lvDesc = format!("lv {}", level);
            match tree {
                &Leaf(val) => println!("{}-{} leaf: {}", lvDesc, prefix, val),
                &Branch(val, box ref left, box ref right) => {
                    println!("{}-{} node: {}", lvDesc, prefix, val);
                    inner_print("left branch <-", left, level + 1);
                    inner_print("right branch ->", right, level + 1);
                },
                &Null => println!("end"),
            }
        }
        inner_print("root", tree, 0);
    }
    
    fn main() {
        print_tree(&create_binary_search_tree(vec![43, 2, 45, 7, 72, 28, 34, 33]));
    }
    

    I verified this code to work in "http://play.rust-lang.org/"