I have these two binary search tree search functions. My question is which one of these functions are more efficient, or if they are equivalent?
Given
type 'a tree =
| Empty
| Node of 'a * 'a tree * 'a tree
let rec search x t =
match t with
| Empty -> Empty
| Node(a, left, right) as t' ->
if a = x then t'
else
match search x left with
| Empty -> search x right
| t'' -> t''
let rec search x tree =
match tree with
| Empty -> Empty
| Node (root, left, right) as t ->
if (x = root) then t
else if (x < root) then search x left
else search x right
I think that the second one is equivalent simply because we don't need to have another match with Empty since we already have it before.
It depends
This really depends on whether values in your binary tree are strictly ordered. Your first function will search the right branch if the value is not found in the left branch. The second will only follow either the left or the right branch, but not both.
They are not equivalent.
Performance and stack considerations
If we assume strict ordering then the first function will be less efficient as it will search all nodes. Performance will be O(n) while with the second function, performance will be O(log n). The first function is also not tail-recursive, so you run the risk of a stack overflow for a very large tree.
If you want to make this tail-recursive, one approach is to maintain a stack or worklist of nodes to deal with and work through them as we go. We search left branches first, pushing the right branches onto the stack. Once we hit an
Emptyleft branch, we start consuming the stack.We can hide the
stackargument by making this a local function.A further suggestion
If we can assume strict ordering in the tree, I would probably suggest the following, using conditional guards.
Which we can reduce to: