This submission to Programming Praxis gives an O(n) function that "undoes" a preorder traversal of a binary search tree, converting a list back into a tree. Supplying the missing data declaration:
data Tree a = Leaf | Branch {value::a, left::Tree a, right:: Tree a} deriving (Eq, Show) fromPreOrder :: Ord a => [a] -> Tree a fromPreOrder [] = Leaf fromPreOrder (a:as) = Branch a l (fromPreOrder bs) where (l,bs) = lessThan a as lessThan n [] = (Leaf,[]) lessThan n all@(a:as) | a >= n = (Leaf,all) | otherwise = (Branch a l r,cs) where (l,bs) = lessThan a as (r,cs) = lessThan n bs
It's obvious that one constructor is added to the tree in each recursive step, which is key to its efficiency.
The only "problem" is that the list is threaded through the computation manually, which is not a terribly Haskellian way to do it and makes it a little harder to see that it is actually consumed element by element in a single-threaded manner.
I attempted to correct this using a state monad (prettified on Codepad):
import Control.Monad.State data Tree a = Leaf | Branch {root::a, left::Tree a, right::Tree a} deriving (Eq,Show) peek = State peek' where peek' [] = (Nothing,[]) peek' a@(x:_) = (Just x,a) pop = State pop' where pop' [] = error "Tried to read past the end of the list" pop' (_:xs) = ((),xs) prebuild'::Ord a => State [a] (Tree a) prebuild' = do next <- peek case next of Nothing -> return Leaf Just x -> do pop leftpart <- lessThan x rightpart <- prebuild' return (Branch x leftpart rightpart) lessThan n = do next <- peek case next of Nothing -> return Leaf Just x -> do if x < n then do pop leftpart <- lessThan x rightpart <- lessThan n return (Branch x leftpart rightpart) else return Leaf prebuild::Ord a => [a] -> Tree a prebuild = evalState prebuild'
Unfortunately, this just looks obscenely messy, and doesn't seem any easier to reason about.
One thought I haven't been able to get anywhere with yet (in part because I don't have a deep enough understanding of the underlying concepts, quite likely): could I use a left fold over the list that builds a continuation that ultimately produces the tree? Would that be possible? Also, would it be anything short of insane?
Another thought was to write this as a tree unfold, but I don't think it's possible to do that efficiently; the list will end up being traversed too many times and the program will be O(n^2).
Edit
Taking things from another direction, I have the nagging suspicion that it might be possible to come up with an algorithm that starts by splitting up the list into increasing segments and decreasing segments, but I haven't yet found something concrete to do with that idea.