180 likes | 277 Views
Functional Programming Lecture 8 - Binary Search Trees. Binary Search Trees. A bst is a tree whose elements are ordered. A general tree is defined by: data Tree a = Nil | Node a (Tree a) (Tree a)
E N D
Binary Search Trees A bst is a tree whose elements are ordered. A general tree is defined by: data Tree a = Nil | Node a (Tree a) (Tree a) We can use this type for bst’s provided a is an Ord type and all operations either create or preserve the ordering. This means that an arbitrary t :: Tree a may not be a bst. We need to check that it is a bst.
Checking a Binary Search Tree A bst must have ordered elements, and no repeated elements; i.e. all values in left subtree are smaller than root and all values in right subtree are larger than root. isbst :: (Ord a) => Tree a -> Bool isbst Nil = True Many ways to define isbst for nodes. Here is one: isbst (Node n Nil Nil) = True isbst (Node n Nil t2) = (n < root t2) && isbst t2 isbst (Node n t1 Nil) = (n > root t1) && isbst t1 isbst (Node n t1 t2) = (n > root t1) && (n < root t2) && isbst t1 && isbst t2 root :: Tree a -> a root (Node n _ _) = n
Checking a Binary Search Tree Is this function sufficient? If a tree is a bst then isbst returns True, but if it isn’t a bst, does it return False? Consider the tree 4 2 10 8 22 3 9 bad = (Node 4 (Node 2 Nil Nil) (Node 10 (Node 8 (Node 3 Nil Nil) (Node 9 Nil Nil)) (Node 22 Nil Nil))) (
Checking a Binary Search Tree To check for bst, whenever we take a right branch, we establish a minimum, and whenever we take a left branch, we establish a maximum. So, we have the following, when considering a node in a path: • if only root, no min or max yet established • if only left branches so far, then we have a max • if only right branches so far, then we have a min • if left and right branches so far, then we have min and a max 4 2 10 When we consider 8, max= 10 min = 4 8 22 When we consider 3, max= 8 min = 4 3 9
Checking a Binary Search Tree data Tree a = Nil | Node a (Tree a) (Tree a) isbst :: Ord a => (Tree a) -> Bool -- is a binary search tree -- top level function isbst Nil = True isbst (Node x Nil Nil) = True isbst (Node x Nil t2) = (root t2)>x && (isbstr t2 x) -- 1st right isbst (Node x t1 Nil) = (root t1)<x && (isbstl t1 x) -- 1st left isbst (Node x t1 t2 ) = (root t1)<x && (root t2)>x && (isbstl t1 x) && (isbstr t2 x) -- 1st right and left
Checking a Binary Search Tree isbstr :: Ord a => (Tree a) -> a -> Bool -- is a binary search tree -- searching a right only branch -- check node is greater than a min -- refine min if going right only isbstr (Node x Nil Nil) min = (x > min) isbstr (Node x Nil t2) min = (x > min) && (root t2)>x && (isbstr t2 x) -- still right only -- refine min isbstr (Node x t1 Nil) min = (x > min) && (root t1)<x && (isbstminmax t1 min x) isbstr (Node x t1 t2 ) min = (x > min) && (root t1)<x && (root t2)>x && (isbstr t2 x) && (isbstminmax t1 min x) -- refine min
Checking a Binary Search Tree isbstl :: Ord a => (Tree a) -> a -> Bool -- is a binary search tree -- searching a leftonly branch -- check node is less than a max -- refine max if going left only isbstl (Node x Nil Nil) max = (x < max) isbstl (Node x Nil t2) max = (x < max) && (root t2)>x && (isbstminmax t2 x max ) isbstl (Node x t1 Nil) max = (x < max) && (root t1)<x && (isbstl t1 x) -- still left only -- refine max isbstl (Node x t1 t2 ) max = (x < max) && (root t1)<x && (root t2)>x && (isbstminmax t2 x max) && (isbstl t1 x) -- refine max
isbstminmax :: Ord a => (Tree a) -> a -> a -> Bool -- is a binary search tree -- searching a branch which is left and right -- check node is less than a max and greater than a min -- refine max and min isbstminmax (Node x Nil Nil) min max = (x > min) && (x < max) isbstminmax (Node x Nil t2) min max = (x > min) && (x < max) && (root t2)>x && (isbstminmax t2 x max ) isbstminmax (Node x t1 Nil) min max = (x > min) && (x < max) && (root t1)<x && (isbstminmax t1 min x ) isbstminmax (Node x t1 t2) min max = (x > min) && (x < max) && (root t1)<x && (root t2) >x && (isbstminmax t1 min x ) && (isbstminmax t2 x max ) root :: Tree a -> a root (Node x _ _ ) = x --
good :: Tree Int good = (Node 4 (Node 2 Nil Nil) (Node 10 (Node 8 (Node 6 Nil Nil) (Node 9 Nil Nil)) (Node 22 Nil Nil))) bad :: Tree Int bad = (Node 4 (Node 2 Nil Nil) (Node 10 (Node 8 (Node 3 Nil Nil) (Node 9 Nil Nil)) (Node 22 Nil Nil))) -- Main> isbst good -- True -- Main> isbst bad -- False --Main>
Insertion To insert x into bst t - Do not insert x if x is already in t. - Insert x in left subtree of t if x<root t. - Insert x in right subtree of t if x>root t. E.g. insert 5 into 4 => 4 2 6 2 6 5
Insertion insert :: Ord a => a -> Tree a -> Tree a -- insert a element in a bst at correct position insert x Nil = Node x Nil Nil insert x (Node n t1 t2) | (x==n) = Node n t1 t2 | (x<n) = Node n (insert x t1) t2 | (x>n) = Node n t1 (insert x t2) Insert 5 (Node 4 (Node 2 Nil Nil) (Node 6 Nil Nil))
Deletion To delete x from bst t. - If x < root t, then delete x in left subtree of t. - If x > root t, then delete x in right subtree of t. - If x==root t and if either subtree is Nil, return the other subtree. - If x==root t, and both subtrees are non-Nil, return the conjoined subtrees. E.g. delete 2 4 4 2 5 => 5 delete 2 4 => 4 2 5 1 5 1 delete 7 7 8 2 9 => 2 9 8
Auxiliary Functions isNil :: Tree a -> Bool -- determine whether a bst is empty isNil Nil = True isNil (Node n t1 t2) = False mintree :: Ord a => Tree a -> Maybe a -- return smallest element in a bst -- treat error properly! n mintree Nil = Nothing t1 t2 mintree (Node n t1 t2) = if (isNil t1) then (Just n) else (mintree t1) or, in another style: mintree t Maybe a = Nothing | isNil t = Nothing | Just a | isNil t1 = Just v | otherwise = mintree t1 where v = root t t1 = left t
Auxiliary Functions join :: (Ord a) =>Tree a -> Tree a -> Tree a -- join together two non-empty bst’s -- the root is the minimum of the right subtree -- be careful with error values! Temptation to write: join t1 t2 = Node (mintree t2) t1 (delete (mintree t2) t2) -- new root left subtree new right subtree Why is this wrong? Because, mintree returns a Maybe type! join t1 t2 = Node m t1 t3 where (Just m) = mintree t2 t3 = delete m t2
Deletion delete :: (Ord) a => a -> Tree a -> Tree a -- delete an item from a bst delete x Nil = Nil delete x (Node n t1 t2) | (x < n) = Node n (delete x t1) t2 | (x > n) = Node n t1 (delete x t2) | isNil t1 = t2 -- x == n | isNil t2 = t1 -- x == n | otherwise join t1 t2 -- x == n
Adding Size to BSTs It is computationally expensive to compute the size of tree: size :: Tree a -> Int size Nil = 0 size (Node n t1 t2) = 1 + size t1 + size t2 If we have to do this operation often, then it is better to add an extra field to store the size. How can we do this? data STree a = Nil | Node a Int (STree a) (STree a) How will operations change?
Adding Size to BSTs insert :: Ord a => a -> STree a -> STree a -- insert a element in a sized bst at correct position -- assume element is not already in bst insert x Nil = Node x 1 Nil Nil insert x (Node n s t1 t2) | (x<n) = Node n (1 + s) newt1 t2 | (x>n) = Node n (1 + s) t1 newt2 where newt1 = insert x t1 newt2 = insert x t2 Note: You can extend this idea. For example, store maximum or minimum of subtrees store value of parent.