Reputation: 927
I'm trying to write a method to delete an element from a BST. So far, this is what I have. I'm not sure if I'm on the right track or if there is a better way to do it by using pattern matching to match the different delete cases ie: no children, 1 child, 2 children.
type 'a bst = NL | BinTree of 'a * 'a bst * 'a bst;;
let rec smallest = function
| NL -> failwith "tree is empty"
| BinTree(m, lst, rst) -> if lst = NL then BinTree(m, lst, rst)
else smallest lst;;
let rec smallest2 = function
| NL -> failwith "tree is empty"
| BinTree(m, lst, rst) -> if lst = NL then m
else smallest2 lst;;
let rec rem2 = function
| NL -> NL
| BinTree(m, NL, NL) -> NL
| BinTree(m, NL, rst) -> rst
| BinTree(m, lst, NL) -> lst
| BinTree(m, lst, rst) -> BinTree(smallest2 rst, lst, rst);;
let rec rem x = function
|NL -> failwith "Node doesn't exit"
|BinTree(m, lst, rst) -> if m = x then rem2 (BinTree(m, lst, rst))
elif m < x then BinTree(m, lst, rem x rst)
else BinTree(m, rem x lst, rst);;
The cases of no children and a single child work perfectly, but when the node to be deleted has 2 children, I cannot figure out how to implement this case. I want to replace that node's value with the smallest item on it's right subtree, and then remove the smallest item on its right subtree.
Upvotes: 1
Views: 986
Reputation: 3996
I have followed the steps Tomas decribes in his post and I came up with this solution:
// BST - binary search tree
type BST<'a when 'a: comparison> = | Leaf
| Node of BST<'a> * 'a * BST<'a>
let rec rmMaxBST = function
| Leaf -> failwith "Tree was empty"
| Node(tL, x, Leaf) -> x, tL
| Node(tL, x, tR ) -> let m, newTR = rmMaxBST tR
m, Node(tL, x, newTR)
let rec rmMinBST = function
| Leaf -> failwith "Tree was empty"
| Node(Leaf, x, tR) -> x, tR
| Node(tL, x, tR) -> let m, newTL = rmMinBST tL
m, Node(newTL, x, tR)
let mergeBST t1 t2 =
match t1, t2 with
| (Leaf, Leaf) -> Leaf
| (t1, Leaf) -> let x, t = rmMaxBST t1
Node(t, x, Leaf)
| (t1, t2 ) -> let x, t = rmMinBST t2
Node(t1, x, t)
let rec delBST x = function
| Leaf -> Leaf
| Node(tL, a, tR) when x < a -> Node(delBST x tL, a, tR)
| Node(tL, a, tR) when a < x -> Node( tL, a, delBST x tR)
| Node(tL, _, tR) -> mergeBST tL tR
I tried this in the REPL:
> delBST 3 Leaf;;
val it : BST<int> = Leaf
> delBST 3 (Node(Leaf, 4, Leaf));;
val it : BST<int> = Node (Leaf,4,Leaf)
> delBST 3 (Node(Leaf, 3, Leaf));;
val it : BST<int> = Leaf
> delBST 3 (Node(Node(Leaf, 1, Leaf), 3, Node(Leaf, 5,Leaf)));;
val it : BST<int> = Node (Node (Leaf,1,Leaf),5,Leaf)
> delBST 1 (Node(Node(Leaf, 1, Leaf), 3, Node(Leaf, 5,Leaf)));;
val it : BST<int> = Node (Leaf,3,Node (Leaf,5,Leaf))
> delBST 5 (Node(Node(Leaf, 1, Leaf), 3, Node(Leaf, 5,Leaf)));;
val it : BST<int> = Node (Node (Leaf,1,Leaf),3,Leaf)
Upvotes: 0
Reputation: 243096
I'm not quite sure I understand the logic that your remove
function is trying to implement. The usual way to do this is to write a recursive function that:
x
is smaller than the current, remove x
from the left sub-tree recursivelyx
is larger than the current, remove x
from the rightsub-tree recursivelyx
is equal to the current, drop the current node and merge the two treesThe way to encode this in F# is to write a recursive function using pattern matching - quite similar to the functions you wrote:
let rec remove x = function
| NL -> NL
| BinTree(m, lst, rst) when x = m -> merge lst rst
| BinTree(m, lst, rst) when x < m -> BinTree(m, remove x lst, rst)
| BinTree(m, lst, rst) (* x > m *) -> BinTree(m, lst, remove x rst)
[EDIT The following is not actually going to work!] This is almost complete, but you need to add the merge
function. The logic for the merge function is the following:
Bin(n, llst, lrst)
and the right is rst
then return a tree that contains n
with llst
on the left and (recursively) merged lrst
and rst
on the right (because elements in both of them are larger than n
).Bin
and left is anything else.This is not going to produce a balanced binary tree, but it is a good start.
EDIT: I think that perhaps an easiest option is to write two functions - one to remove the largest and one to remove the smallest element of the tree (then when merging, you can just call one of these two). This might be easier than writing a fully general remove function.
The following removes the largest element and returns it, together with a new tree (without the largest element):
let rec remLargest = function
| NL -> failwith "Tree was empty!"
| BinTree(m, l, NL) -> m, l
| BinTree(m, l, r) ->
let res, newR = remLargest r
res, BinTree(m, l, newR)
Upvotes: 3