Trees

Consider the following Binary Tree type

data Tree a = Leaf | Node (Tree a) a (Tree a)

1 inClass

Write the function elemTree that tests if an element x occurs in a binary search tree of type Tree a.

elemTree :: Ord a => a -> Tree a -> Bool
elemTree q t = succOf q t == q

-- or directly:

elemTree :: Ord a => a -> Tree a -> Bool
elemTree _ Leaf = False
elemTree q (Node l x r) | q < x     = elemTree q l
                        | q == x    = True
                        | otherwise = elemTree q r

2 atHome

Write functions that return all values of type a in a tree of type Tree a in depth-first (pre-order), depth-first (in-order), and breadth-first order.

traverseTree                      :: ([a] -> a -> [a] -> [a]) -> Tree a -> [a]
traverseTree _       Leaf         = []
traverseTree combine (Node l x r) =
  combine (traverseTree combine l) x (traverseTree  combine r)

preOrderTraversal :: Tree a -> [a]
preOrderTraversal = traverseTree (\l x r -> x : (l ++ r))

inOrderTraversal :: Tree a -> [a]
inOrderTraversal = traverseTree (\l x r -> l ++ (x : r))

3 atHome

Write a function showTree that gives a nice representation as a String for a given tree. Every leaf should be placed on a different line (separated by "\n"). Leaves that are deeper in the tree should be further to the right than leaves higher in the tree.

4 inClass

Write a function mapTree and a function foldTree that work on a Tree, analoguous to map and foldr on lists. Also give the type of these functions.

mapTree                :: (a -> b) -> Tree a -> Tree b
mapTree _ Leaf         = Leaf
mapTree f (Node l x r) = Node (mapTree f l) (f x) (mapTree f r)

foldTree                  :: (b -> a -> b -> b) -> b -> Tree a -> b
foldTree _ z Leaf         = z
foldTree f z (Node l x r) = f (foldTree f z l) x (foldTree f z r)

5 inClass

Write a function height, that computes the amount of levels in a Tree. Give a definition using recursion, and a different definition using foldTree.

height = foldTree (\l _ r -> 1 + l `max` r) 1

6 atHome

Suppose that a tree t has height n. What is the minimal and maximal amount of leaves that t could have?

7 atHome

Write a function that computes all paths of type [a] from the root up to a leaf for a tree of type Tree a.

paths              :: Tree a -> [[a]]
paths Leaf         = [[]]
paths (Node l x r) = map (x:) (paths l) ++ map (x:) (paths r)

8 atHome

Write a function that computes a list of nodes that occur on one of the longest paths from the root up to a leaf. Try to keep the solution linear with respect to the size of the tree.

9 atHome

Write mapTree using foldTree

mapTree f = foldTree (\l x r -> Node l (f x) r) Leaf

10 inClass

Write a function extractMin that removes the smallest element from a binary search tree. Returns the smallest element and the remainder of the tree.

extractMin              :: Tree a -> Maybe (a, Tree a)
extractMin Leaf         = Nothing
extractMin (Node l k r) = case extractMin l of
                            Nothing     -> Just (k, r)
                            Just (m,l') -> Just (m, Node l' k r)

11 inClass

Write a function delete that deletes an element from the tree. You can assume the element appears at most once. Your new tree should remain a binary search tree.

Hint: You may want to use the function extractMin you defined above.

delete                :: Ord a => a -> Tree a -> Tree a
delete _ Leaf         = Leaf
delete x (Node l k r) = case x `compare` k of
                          LT -> Node (delete x l) k r
                          EQ -> case extractMin r of
                                  Nothing     -> l
                                  Just (m,r') -> Node l m r'
                                    -- we replace k by its successor.
                          GT -> Node l k (delete x r)

12 inClass

Define a binary tree type LeafTree in which leaves store values of type a and internal nodes store values of type b

data LeafTree a b = Leaf a
                  | Node (LeafTree a b) b (LeafTree a b)
                  deriving (Show,Eq)

13 atHome

Define a function height that given a LeafTree computes the height of the LeafTree a b. Leaves have height one.

height              :: LeafTree a b -> Int
height (Leaf _)     = 1
height (Node l _ r) = 1 + max (height l) (height r)

14 inClass

Define a function annotate, that given a LeafTree Int b labels every internal node with the minimum and maximum value appearing in the leaves in its subtree.

e.g.

Node (Node (Leaf 3) "left" (Leaf 5))
     "root"
     (Leaf 1)

->

Node (Node (Leaf 3) (3,5) (Leaf 5))
     (1,5)
     (Leaf 1)
annotate   :: LeafTree Int b -> LeafTree Int (Int,Int)
annotate t = case annotate' t of
               (t',_,_) -> t'

-- | Computes the annotated tree, as well as the minimum and maximum in the tree
annotate'              :: LeafTree Int b -> (LeafTree Int (Int,Int), Int, Int)
annotate' (Leaf x)     = (Leaf x, x, x)
annotate' (Node l _ r) = let (l',lmin,lmax) = annotate' l
                             (r',rmin,rmax) = annotate' r
                             mi             = min lmin rmin
                             ma             = max lmax rmax
                         in (Node l' (mi,ma) r', mi, ma)

15 atHome

Write a function withDepth that annotates the tree by its depth. The depth of a node v is the length of the path from the root to v.

withDepth :: LeafTree a b -> LeafTree (a,Int) (b,Int)
withDepth = withDepth' 0

withDepth'                :: Int -> LeafTree a b -> LeafTree (a,Int) (b,Int)
withDepth' d (Leaf x)     = Leaf (x,d)
withDepth' d (Node l x r) = Node (withDepth' (d+1) l) (x,d) (withDepth' (d+1) r)

16 atHome

Write a function trimWhen :: (a -> Bool) -> (b -> Bool) -> LeafTree a b -> LeafTree (LeafTree a b) b that ‘trims’ the subtree depending on two predicates p :: a -> Bool and q :: b -> Bool . In particular, predicate p x returns True if and only if we should trim the leaf storing some value x and q y returns True if and only if the subtree whose root stores a y should be trimmed.

trimWhen       :: (a -> Bool) -> (b -> Bool) -> LeafTree a b
               -> LeafTree (Maybe (LeafTree a b)) b
trimWhen p q t = case t of
                   Leaf x | p x           -> Leaf (Just t)
                          | otherwise     -> Leaf Nothing
                   Node l y r | q y       -> Leaf (Just t)
                              | otherwise -> Node (trimWhen p q l) y (trimWhen p q r)

17 atHome

Write a function bimapTree :: (a -> c) -> (b -> d) -> LeafTree a b -> LeafTree c d.

bimapTree       :: (a -> c) -> (b -> d) -> LeafTree a b -> LeafTree c d
bimapTree f g t = case t of
                    Leaf x     -> Leaf (f x)
                    Node l x r -> Node (bimapTree f g l) (g x) (bimapTree f g r)

18 atHome

Write a function trim :: Int -> LeafTree a b -> LeafTree (Maybe (LeafTree a b) b) that trims a tree at the given depth.

trim   :: Int -> LeafTree a b -> LeafTree (Maybe (LeafTree a b)) b
trim d = bimapTree f fst . trimWhen p p . withDepth
  where
    p        :: (c,Int) -> Bool
    p (_,d') = d' == d

    f Nothing  = Nothing
    f (Just t) = Just $ bimapTree fst fst t

19 atHome

Define a Tree type TTTree a that models trees in which

data TTTree a = Leaf a
              | Node2 Int (TTTree a) (TTTree a)
              | Node3 Int (TTTree a) (TTTree a) (TTTree a)

20 atHome

Define a function insert that inserts a new element in a TTTree, while maintaining the subtree size invariant, and while keeping the height low (e.g. try to avoid increasing the height if possible).

21 atHome

Define mapTTTree and foldTTTree functions for your TTTree data type.

foldTTTree           :: (a -> b) -> (Int -> b -> b -> b) -> (Int -> b -> b -> b -> b) -> TTTree a -> b
foldTTTree f g2 g3 t = case t of
                         Leaf x        -> f x
                         Node2 s l r   -> g2 s (foldTTTree f g2 g3 l) (foldTTTree f g2 g3 r)
                         Node3 s l m r -> g3 s (foldTTTree f g2 g3 l) (foldTTTree f g2 g3 m) (foldTTTree f g2 g3 r)

mapTTTree f = foldTTTree (Leaf . f) Node2 Node3

22 atHome

Define function sized that, given an Int \(n\), returns all subtrees of size \(n\).

23 atHome

It is not possible to write the sized function directly using foldTTTree. However, we can use foldTTTree to do most of the work; that is, we can define a function sized' using foldTTTree such that

sized   :: Int -> TTTree a -> [TTTree a]
sized n = snd . sized' n

write the function sized'

sized   :: Int -> TTTree a -> (TTTRee a, [TTTree a])
sized n = foldTTTree f g2 g3
  where
    singleton s t = if n == s then [t] else []

    f x = let t = Leaf x
          in (t,singleton s t)

    g2 s (l,ls) (r,rs) = let t = Node2 s l r
                         in (t, singleton s t ++ ls ++ rs)

    g3 s (l,ls) (m,ms) (r,rs) = let t = Node3 s l m r
                                in (t, singleton s t ++ ls ++ ms ++ rs)

24 atHome

A TTTree is valid if all root to leaf paths are equally long. Write a function height that computes the height of a TTtree if it is valid. Think about a suitable type for your function.

height :: TTTree a -> Maybe Int
height = foldTTTree (\_          -> Just 0)
                    (\_ lh rh    -> inc $ lh <.> rh)
                    (\_ lh mh rh -> inc $ lh <.> mh <.> rh)
  where
    inc Nothing  = Nothing
    inc (Just h) = Just (h+1)

    Nothing <.> _                   = Nothing
    Just h  <.> Nothing             = Nothing
    Just h  <.> Just hr | h == hr   = Just h
                        | otherwise = Nothing

25 Red-Black Trees atHome

Write a function validRBTree :: RBTree a -> Bool that checks if a given RBTree a satisfies all red-black tree properties.

26 atHome

Consider the following data type of binary trees BST a that we intend to use to implement binary search trees. We will store “real” elements (the elements we care about) only in the Leaves of the tree. The a field in a Node is used only as a routing element (to guide searches).

data BST a = Leaf a
           | Node (BST a) a (BST a)
           deriving (Show,Read,Eq)

We can compute the elements we care about of such a tree as follows:

elems :: BST a -> [a]
elems (Leaf x) = [x]
elems (Node l _ r) = elems l ++ elems r

An example of such a trees we will care about is

exampleTree = Node (Node (Leaf 1) 3 (Node (Leaf 4) 6 (Leaf 7))) 8 (Node (Leaf 9) 10 (Leaf 13))

Then elems exampleTree = [1, 4, 7, 9, 13]

Here, the only purpose of the element 8, for example, is to signal that all the elements in the left subtree are \(\leq 8\) and that all the elements in the right subtree are \(> 8\).

The question: write a function complete that constructs a complete balanced binary search tree out of an sorted list of \(2^h\), with \(h \geq 0\), elements (with those elements in the same order as given.).

-- | This runs in O(n\log n) time.
complete :: [a] -> BST a
complete [x] = Leaf x
complete xs  = let (ls,rs) = splitAt (n `div` 2) xs
                   n       = length xs
               in Node (complete ls) (last ls) (complete rs)

Bonus: Write complete so that it runs in linear time.

27 atHome

Give the type of a function delete, such that, given an element a and tree t, delete a t looks whether a is present in t and if so, removes a from t. The function delete should always be able to produce a valid element of the result type, i.e. it may not produce an error.

delete :: Ord a => a -> BST a -> Maybe (BST a)

28 atHome

Please implement the above function delete.

delete x t = case t of
  Leaf y | x == y        -> Nothing
         | otherwise     -> Just t
  Node l k r | x <= k    -> Just $ case delete x l of
                              Nothing -> r
                              Just l' -> Node l' k r
             | otherwise -> Just $ case delete x r of
                              Nothing -> l
                              Just r' -> Node l k r'

29 atHome

Use higher order functions (i.e. foldr, foldl', map, filter) to write a function batchDelete that removes all elements from a given list from a tree. Give the type of this function as well.

batchDelete      :: Ord a => [a] -> BST a -> Maybe (BST a)
batchDelete xs t = foldr (\x mt -> mt >>= \t -> delete x t) (Just t) xs
  where
    -- This funny operator is called "bind" and is actually defined in
    -- the prelude. It is the part of the Monad instance/definition of
    -- the 'Maybe' type.
    (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
    Nothing  >>= _ = Nothing
    (Just x) >>= f = f x