In the previous post, we implemented a simple Binary Search Tree(BST) and added functionality to
add/search and traverse binary tree. In this post we will add functionality to remove
keys from BST. Deleting a key in binary tree is little difficult, to delete a node
from tree we need to consider following cases.
The Tree is Nil, Then after deleting resulting tree is Nil.
If the Node has matching key and it’s left and right children are Nil, then resulting tree is Nil tree.
If nodes right children in Nil
If key matches the resulting tree is left tree.
else if node’s key is bigger then try deleting search key from left tree
otherwise search key do not exists in tree, tree remains as it is.
Symmetric to case 3, for left children.
If node does not have matching key, and node’s key is bigger than search key, then try deleting from left subtree, else try deleting from right subtree.
The complex case is when, Node key is matching and it’s right and left subtrees are not Nil, In this case we will pick up maximum key from left subtree and replace it with current node, and delete maximum key from the left tree.
The resulting Haskell implementation is below.
1234567891011121314151617181920212223242526
delete::(Orda)=>Treea->a->Treea-- case 1deleteNil_=Nil-- case 2delete(NodeaNilNil)key|a==key=Nil|otherwise=NodeaNilNil-- case 3delete(NodeaNilright)key|a==key=right|a<key=NodeaNil(deleterightkey)|otherwise=NodeaNilright-- case 4delete(NodealeftNil)key|a==key=left|a>key=Nodea(deleteleftkey)Nil|otherwise=NodealeftNil-- case 5 and 6 delete(Nodealeftright)key|a==key=NodemaxLeft(deleteleftmaxLeft)right|a>key=Nodea(deleteleftkey)right|otherwise=Nodealeft(deleterightkey)wheremaxLeft=maxElemNonNilleftmaxElemNonNil(Nodea_Nil)=amaxElemNonNil(Nodea_left)=maxElemNonNilleft
I have just started to learn Haskell from various sources, such as
Lean You Haskell and Real World Haskell. After reading
few chapters, this is my first attempt to code a Binary Search Tree in
Haskell, supporting add, search and traverse operation.
A node is Binary Search Tree contains a comparable key and left and
right subtrees, which can be empty. The left tree contains
smaller keys and right tree contains bigger keys that the node’s key.
The Tree is recursively defined as a Nil tree i.e a empty tree, or a Node with key, left and right subtrees. This recursive
data definition is specified in Haskell easily using abstract data type as follows.
To add a key in a BST is easy, if tree is empty, then after adding
one key is equivalent of creating a new Node with left and right tree
set to Nil, and data set to the key.
If new key is smaller than current node’s key then add a key to the left
tree, else add the key to right tree.
The following function implements the insert method which takes a Tree
and key to be added to the tree. If key is already present in the Tree
then this function returns the same Tree, else returns a new Tree with
key added. As Haskell is purely functional language, we can not modify
original tree, instead it creates a new tree every time a key is added
to the tree.
12345678910
{-Add a node to Tree, If element already present in the Tree,then it is ignored.-}insert::(Orda)=>Treea->a->TreeainsertNilelem=NodeelemNilNilinsert(Nodevalleftright)elem|elem<val=Nodeval(insertleftelem)right|elem>val=Nodevalleft(insertrightelem)|otherwise=Nodevalleftright
If we want to add bunch of keys to the Tree, then built in fold
function can be used, A one line function to add list of keys to
the Tree is
Searching in binary tree starts at the root node and then recursively
follow left or right branch until we reach at empty tree (Nil) or
search key is found. The function is split into two cases.
If current root is Nil then we have reached at the leaf node, and element
does not exists in the tree, so we return false. (line 3)
If current root is not Nil and key matches then element present in the tree,
else we recursively call search with left or right node depending on value of
key. (line 5,6,7)
1234567
{- Search a tree -}search::(Orda)=>Treea->a->BoolsearchNil_=Falsesearch(Nodeelemleftright)a|elem==a=True|elem>a=searchlefta|otherwise=searchrighta
Apart from breath first and depth first traversal, binary tree can be
traverse in pre-order, post-order and in-order traversal. The in-order
traversal visit elements in the sorted order. Following is the
implementation in Haskell which returns a list of element in order
they are visited in the traversal.
Pre-order
Visit the root.
Traverse the left subtree.
Traverse the right subtree.
12345
{- Pr-eorder tree traversal -}preOrder::Treea->[a]preOrderNil=[]preOrder(Nodealeftright)=a:preOrderleft++preOrderright
In-order (symmetric)
Traverse the left subtree.
Visit the root.
Traverse the right subtree.
12345
{- In-order tree traversal -}inOrder::Treea->[a]inOrderNil=[]inOrder(Nodealeftright)=inOrderleft++[a]++inOrderright
Post-order
Traverse the left subtree.
Traverse the right subtree.
Visit the root.
12345
{- Post-order tree traversal -}postOrder::Treea->[a]postOrderNil=[]postOrder(Nodealeftright)=postOrderleft++postOrderright++[a]
Lets try these functions, see that in-order traversal of BST gives
element in sorted order.
The maximum element is the right most element in the tree, i.e keep following
right branch until node’s right branch is Nil. That node has the maximum value
of the key.
That’s it for now, In this post we have implemented a simple binary tree supporting basic operations except delete for which I am planing
to write a separate post. Haskell language is very concise, and because of pattern matching and good support for writing recursive functions
, most of the recursive algorithms on binary tree are translated directly into Haskell without much effort.
The code for binary search tree is available on Github