Binarysearch in sml - binary-tree

datatype 'a tree= Leaf of 'a | Node of 'a tree * 'a * 'a tree
fun binSearch (Node(left,n,right)) x =
if x > n then false
else if x=n then true
else binSearch (Node(left,n,right)) x = binSearch (right) x andalso binSearch (left) x;
I'm helpless. What's wrong with that code?
BTW it works for:
binSearch (Node (Node (Leaf 1, 2, Leaf 3), 4, Leaf 7)) 7;
and doesn't work for:
binSearch (Node (Node (Leaf 1, 2, Leaf 3), 4, Leaf 7)) 2;

Your definition of a binary tree isn't ideal, since (for example) you can't construct a tree with two elements. A more flexible and generally simpler definition is
datatype 'a tree = Leaf | Node of 'a tree * 'a * 'a tree
where Leaf is a tree with no elements. This lets you write very simple base cases:
fun binSearch t x =
case t of
Leaf => false
| Node (left, n, right) => ...
For the Node case, you might find it helpful to structure your code according to the three possibilities of comparing the the desired value x to the current value n.
fun binSearch t x =
case t of
Leaf => false
| Node (left, n, right) =>
case Int.compare (x, n) of
LESS => ...
| EQUAL => true
| GREATER => ...
I'll leave the rest to you.

In the first condition, you're saying that no elements smaller than x exist in any tree.
You forgot to write a case for the empty tree, which is the only case where you know whether a value definitely isn't found:
binSearch (Leaf n) x = n = x
Also, your last else,
binSearch (Node(left,n,right)) x = binSearch (right) x andalso binSearch (left) x
is a comparison of whether binSearch (Node(left,n,right)) x gives the same result as binSearch right x andalso binSearch left x.
That is, it says that x is found in the tree if it's either in the entire tree and both its subtrees, or not found anywhere at all.
The recursion should go
If x is the value n in the node, we've found it.
Otherwise,
If x < n, recurse into the left subtree,
If x > n, recurse into the right subtree
Implementation left as an exercise.

Related

Prove/explain that the time complexity of the algorithm is O(h+k)

Prove/explain that the time complexity of the given algorithm is O(h+k). Where h is the height of the tree and k is the number of nodes in range between x and y (inclusive).
I know that if k=0 (no items in range) then it the algorithm simply traverses through the height of the tree thus O(h). I also know if a node is in the range it recurs to both of its children as opposed to just one of them. But this seems to have a doubling effect, so I am confused on how to prove/explain this.
INRANGE-TREE-WALK (v, x, y)
if v != NIL
if v.key < x
INRANGE-TREE-WALK(v.right, x, y)
else if v.key 〈= y
print v.key
INRANGE-TREE-WALK (v.left, x, y)
INRANGE-TREE-WALK (v.right, x, y)
else
INRANGE-TREE-WALK(v.left, x, y)
I assume the tree is a binary search tree.
You could look at the depth of the current node with which the function is called, and then notice that for any given depth, never more than one node v with that depth is visited for which v.key < x.
Although that can happen many times on different depths, the claim is that this will never happen more than once at the same depth in the tree.
Assume for a moment this claim is false, and that at one specific depth d in the tree the algorithm visits two nodes a and b, and it finds that both a.key < x and b.key < x. Let's also choose a and b such that a.key <= b.key, i.e. a is at the left of b.
First, notice that the only way to visit both a and b is to first visit a common ancestor p of these nodes, for which we have x <= p.key <= y. Otherwise there is no way to walk along two different branches down the tree.
But since this is a binary search tree, all nodes v in the right subtree below p have values with v.key >= P.key and by consequence v.key >= x. This is also true for node b. But that is in contradiction with the premise that b.key < x.
So, we have proof that the function will not be called for two nodes on the same level and have v.key < x for both of them. By consequence this condition can only be found true h times in the total execution of the algorithm.
The same conclusion can be drawn for the case v.key > y.
How many times will x <= p.key <= y be true? As the algorithm will never visit the same node twice (as it only visits children of any visited node), the number of times that this condition is true is equal to k.
Remains the number of times that v == NIL. This can never be more than the number of visited nodes plus one.
Add all of this together and we arrive at a number of individual function calls that is not more than 2(2h + k)+1, and given that the execution of one function call -- excluding the recursive calls -- is constant, we get a time complexity of O(h+k).

Binary Tree Find number nearest and greater than the key

Say I have a balanced binary tree. I wish to search for a key k in the tree. However, if k doesn't exist in the binary tree, it should give me the next greatest number nearest to k.
For examples suppose I have these numbers [1,5,6,8,10] as keys in the tree. If I search for '7' it should return 8 and if I search for 2 it should return 5 etc.
What would have to be the modifications in the binary tree to be able to perform such a search? I want an O(log n) solution as well please.
Assuming you mean "binary search tree" rather than "binary tree", you don't need any modifications to find the minimum element y in the tree such that y >= x.
search(n, x, best_so_far) ::=
if n == nil { return best_so_far }
if n.value == x { return x }
if n.value > x { return search(n.left, x, min(best_so_far, n.value) }
if n.value < x { return search(n.right, x, best_so_far) }
You would call this function as search(root, x, +infinity).
The idea is that if you're exploring the left branch at a node n, you don't need to consider anything to the right of n: n.value is larger than x, and everything to the right is larger than n.value. Similarly, if you're exploring the right branch of a node n, then you can discard everything to the left of n: n.value is smaller than x and everything to the left of n is smaller than n.value.
The code's runtime is bounded by the height of the tree, so is O(log n) if the tree is balanced.

Finding size of largest subtree of a bst contained in a range

This was a recent interview question. The question asked to find the size of the largest subtree of a BST contained within a range [x, y] were x < y. The BST is defined recursively where each node has an integer value, a left child node, and a right child node. I was only able to the total number of nodes in the tree that lie within the range but was not able to find the largest subtree. Here's the code I used, in python:
def solution(x, y, T):
if T is None:
return 0
size = 0
if x < T.val:
size += solution(x, y, T.left)
if x <= T.val and y >= T.val:
size += 1
# The following if statement was my attempt at resetting the count
# whenever we find a node outside the range, but it doesn't work
if x > T.val or y < T.val:
size = 0
if B > T.x:
size += solution(A, B, T.right)
return size
The solution should be O(N) where N is the number of nodes in the tree.
We can solve the problem recursively. We need to know the left and the right border of each subtree (that is, the smallest and the largest element). If it lies in range [x, y], we can just update the answer with the total size of the current subtree. Here is some code (the solution function returns a tuple with some extra information on top of the answer. If just want it to return the size of the largest subtree in range, you can wrap it around and use it as a helper function).
def min_with_none(a, b):
"""
Returns the minimum of two elements.
If one them is None, the other is returned.
"""
if a is None:
return b
if b is None
return a
return min(a, b)
def max_with_none(a, b):
"""
Returns the maximum of two elements.
If one them is None, the other is returned.
"""
if a is None:
return b
if b is None:
return a
return max(a, b)
def solution(x, y, T):
"""
This function returns a tuple
(max size of subtree in [x, y] range, total size of the subtree, min of subtree, max of subtree)
"""
if T is None:
return (0, 0, None, None)
# Solves the problem for the children recursively
left_ans, left_size, left_min, _ = solution(x, y, T.left)
right_ans, right_size, _, right_max = solution(x, y, T.right)
# The size of this subtree
cur_size = 1 + left_size + right_size
# The left border of the subtree is T.val or the smallest element in the
# left subtree (if it's not empty)
cur_min = min_with_none(T.val, left_min)
# The right border of the subtree is T.val or the largest element in the
# right subtree (if it's not empty)
cur_max = max_with_none(T.val, right_max)
# The answer is the maximum of answer for the left and for the right
# subtree
cur_ans = max(left_ans, right_ans)
# If the current subtree is within the [x, y] range, it becomes the new answer,
# as any subtree of this subtree is smaller than itself
if x <= cur_min and cur_max <= y:
cur_ans = cur_size
return (cur_size, cur_ans, cur_min, cur_max)
This solution clearly runs in linear time as it visits every node only once and performs a constant number of operations per node.
With each node in BST, you can associate a valid range for it say [Li,Ri], which implies that all elements in subtree of that node lie in the valid range.
You can easily define these ranges recursively:
For a node i let's say the range is [Li, Ri] and the value stored in this node is val.
For left child of i, the range is [Li, val − 1]. And similarly for right child the range is [val + 1, Ri].
For root node the valid range is [−inf, inf].
Let's say our size function returned -1 for an invalid subtree. A (sub)tree is valid if both the node's value and all the values in its subtrees are in range.
# returns a tuple: (size of T, best valid size among itself and its subtrees)
def f(x,y,T):
if T is None:
return (0,0)
l_size, l_best = f(x,y,T.left)
r_size, r_best = f(x,y,T.right)
if x <= T.value <= y and l_size >= 0 and r_size >= 0:
return (1 + l_size + r_size,1 + l_size + r_size)
else:
return (-1,max(l_best,r_best))

Binary Search Tree - Searching for a Scope

If I have a binary search tree S with the number pair (a,b) where (a<=b); is there an algorithm that would help me find the elements in S with the key values that are within the range of a,b inclusive ([a,b]).
The runtime restriction is O(h+k), h is the tree height of S, and k is the number of elements within the range.
The classic answer is from "Introduction to Algorithms":
http://staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap14.htm
Step 1: find a, using the normal binary tree lookup.
Step 2: call tree successor iteratively until you find b. Tree successor gives you the next item in the tree:
TREE-SUCCESSOR(x)
if right[x] ≠ NIL
then return TREE-MINIMUM (right[x])
y ← p[x]
while y ≠ NIL and x = right[y]
do x ← y
y ← p[y]
return y
TREE-MINIMUM (x)
while left[x] ≠ NIL
do x ← left[x]
return x

How to find the depth of a binary tree using Prolog

I am learning Prolog and am trying to find the depth of a binary tree using Prolog.
I represent a tree like this:
nil is a tree.
tree(1,nil,nil) this is a leaf.
tree(1,tree(1,nil,nil),nil) this is a tree with root 1 and has a left leaf 1.
I want a depth predicate that depth(T, N) that will be true if N is the depth of the tree T.
I assume that I'll use the depth predicate when T isn't a variable but N can be a variable.
Examples:
?- depth(nil,N).
N = 0.
?- depth(tree(1,tree(2,nil,tree(3,nil,nil)),tree(5,tree(6,nil,nil),nil)),N).
N = 3.
?- depth(tree(1,nil,tree(2,nil,nil)),N).
N = 2.
I am not sure how do that N will be the maximum between the 2 subtrees.
Thanks for any help.
Solution:
depth(nil,0).
depth(tree(_,nil,nil),1).
depth(tree(_,Left,Right), D) :-
depth(Left,DLeft),
depth(Right,DRight),
D is max(DLeft, DRight) + 1.
Easy as pie: the depth of nil is 0:
depth(nil,0).
For the tree case, recursively call depth/2 on both branches and max them:
depth(Left,DLeft),
depth(Right,DRight),
D is max(DLeft, DRight) + 1.
% I added Root \= nil, simulating a null node and it works
depth_tree(nil, 0).
depth_tree(node(Root, Left, Right), Depth):-
Root\= nil,
depth_tree(Left, DepthLeft),
depth_tree(Right, DepthRight),
Depth is max(DepthLeft, DepthRight) + 1.

Resources