{-# LANGUAGE InstanceSigs #-} -- Tree (leaf valued) examples import qualified Data.Functor import qualified Data.Foldable -- define a (polymorphic) data-type of a (leaf-valued) tree data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show,Read) -- can use 'show' and 'read' for out-/in-put -- a function that collects all values from the leaves of the tree fringe :: Tree a -> [a] fringe (Leaf x) = [x] fringe (Branch left right) = fringe left ++ fringe right -- a simple example: compute the depth of the tree depth :: Tree a -> Int depth (Leaf _) = 1 depth (Branch left right) = 1 + max (depth left) (depth right) -- check whether this tree is balanced isBalanced :: Tree a -> Bool isBalanced (Leaf _) = True isBalanced (Branch left right) = (abs (depth left - depth right)) <= 1 && isBalanced left && isBalanced right -- Q: is this an efficient version? what's the complexity in terms of tree depth? can you make it more efficient? -- A: no; for 2^d nodes, depth has a complexity of 2^d, and isBalanced \Sum_{i=0}^{d} 2^i = 2^{i+1}-1 -- a better, but longer solution is this isBalanced1 :: Tree a -> Bool isBalanced1 t = abs (pmax-pmin) <= 1 where pmax = minimum ps pmin = maximum ps ps = allPathLengths t allPathLengths :: Tree a -> [Int] allPathLengths (Leaf _) = [1] allPathLengths (Branch left right) = map (1+) (allPathLengths left ++ allPathLengths right) -- a better solution, avoiding intermediate lists, is this isBalanced2 :: Tree a -> Bool isBalanced2 t = fst3 (isBalanced'' t) where fst3 (x,_,_) = x isBalanced'' :: Tree a -> (Bool, Int, Int) isBalanced'' (Leaf _) = (True, 1, 1) isBalanced'' (Branch left right) = (lb && rb && abs (newmax - newmin) <= 1 , 1+newmin, 1+newmax) where (lb, lmin, lmax) = isBalanced'' left (rb, rmin, rmax) = isBalanced'' right newmin = min lmin rmin newmax = max lmax rmax {- test the performance of both versions like this: *Main> let t8 = mkTree [(1::Int)..99000] *Main> :set +s (*Main> isBalanced t8 True (1.57 secs, 429012280 bytes) *Main> isBalanced1 t8 True (0.54 secs, 268147848 bytes) *Main> isBalanced2 t8 True (0.43 secs, 136741408 bytes) -} -- isSorted cecks whether the leaf values are sorted -- version with explicit recursion over a list isSorted' :: Tree Int -> Bool isSorted' = isSortedList . fringe -- the above version avoids naming the argument, which only comes in from the right (better style) -- the version below is equivalent -- isSorted' t = isSortedList (fringe t) -- aux function: a verbose version of checking whether a list is sorted isSortedList :: [Int] -> Bool isSortedList [] = True isSortedList (x:xs) = isSortedList' x xs where isSortedList' x [] = True isSortedList' x (y:ys) = x<=y && isSortedList' y ys -- aux fct: a more compact, higher-order version of checking whether a list is sorted isSorted :: Tree Int -> Bool isSorted t = and (zipWith (<) xs (tail xs)) where xs = fringe t -- overload the == operator on trees instance (Eq a) => Eq (Tree a) where Leaf a == Leaf b = a == b (Branch l1 r1) == (Branch l2 r2) = (l1==l2) && (r1==r2) _ == _ = False -- insert a new value into a tree, whose leaf values are sorted -- NB: efficiency is poor, due to repeated calculation of fringe -- Exercise: how can you improve either algorithm or tree rep to avoid this insert :: Tree Int -> Int -> Tree Int insert (Leaf x) z | x Tree a -> IO () pp t = mapM_ putStrLn (pp' 0 t) where pp' n (Leaf x) = [(take n (repeat ' ')) ++ (show x)] pp' n (Branch left right) = pp' (n+1) left ++ [(take n (repeat ' ')) ++ ['.']] ++ pp' (n+1) right -- build a balanced tree from a list mkTree :: [a] -> Tree a mkTree [] = error "mkTree: empty list" mkTree [x] = Leaf x mkTree xs = Branch left right where (ls,rs) = splitAt (length xs `div` 2) xs left = mkTree ls right = mkTree rs -- sample trees t1, t2, t10 :: Tree Int t1 = Branch (Branch (Leaf 1) (Leaf 2)) (Branch (Leaf 3) (Leaf 4)) t2 = Branch (Leaf 1) (Branch (Leaf 2) (Branch (Leaf 3) (Leaf 4))) t3 = Branch t1 t2 t10 = mkTree [1..10]