Check if a binary tree is binary search tree or not - YouTube

Channel: mycodeschool

[0]
In this lesson, we're going to solve a simple problem on binary tree
[3]
which is also a famous programming interview question,
[7]
and the problem is given a binary tree
[10]
we need to check if the binary tree is a binary search tree
[14]
are not. As we know a binary tree is a tree
[18]
in which each node can have atmost two children.
[22]
All these trees that I have drawn here are binary trees,
[25]
but not all of them are binary search trees. Binary search tree,
[30]
as we know is a binary tree in which for each node
[33]
value of all the nodes in left subtree is lesser
[37]
and if you want to allow duplicates we can say lesser or
[40]
equal and value of all the nodes in right subtree
[43]
is greater. We can define binary search tree as
[46]
a recursive structure like this. Elements in left subtree must be lesser or equal
[52]
and elements in right subtree must be greater and this should be true for all
[56]
nodes and not just a root node,
[58]
so left and right subtrees should themselves also be binary search trees.
[63]
Of these binary trees that I'm showing here,
[67]
A and C are binary search trees but B
[71]
and D are not. In B for the root node with value 10,
[75]
we have 11 in its left subtree which is greater than 10
[79]
and in a binary tree for any node all values in its
[83]
left subtree must be lesser. In D we are good for the root node.
[87]
The value in root node is 5 and we have
[91]
1 in left subtree which is lesser and we have 8, 9 and 12
[95]
in right subtree which are greater. So we are good for the root node
[99]
but for this node with value 8, we have 9
[103]
in its left. So this tree is not a binary search tree.
[106]
So how should we go about solving this problem. Basically,
[110]
I want to write a function that should take pointer or reference to root
[114]
node of a binary tree as argument and function should return true
[118]
if the binary tree is BST, false otherwise.
[122]
This is how my method signature look like in C++.
[125]
In C, we do not have boolean types so
[129]
return type here can be int. We can return
[132]
1 for true and 0 of false.
[135]
I'll also write the definition of node here.
[138]
For a binary tree node would be structure
[142]
with 3 fields, 1 to store data and 2
[145]
to store addresses of left and right children. In my definition of node here,
[149]
data type is integer
[151]
and we have 2 pointers to node to store addresses of
[155]
left and right children. Okay coming back to the problem
[159]
there are multiple approaches and we're going to talk about
[162]
all of them. The first approach that I'm going to talk about
[165]
is easy to think of but it's not so efficient
[168]
but let's discuss it anyway. We are saying that for a binary tree to be called
[174]
binary search tree,
[175]
it should have recursive structure like this. For the root node
[180]
all the elements in left subtree must be lesser or equal
[183]
and all the elements in right subtree must be greater, and left and right
[187]
subtrees
[188]
should themselves also be binary search trees. So let's just check for all of this.
[194]
I'm going to write a function named IsSubtreeLesser
[197]
that will take address of root node of a binary tree
[201]
or subtree and and integer value
[204]
as argument and this function will return true if
[208]
all the elements in the subtree are lesser so than this value
[212]
and similarly I'll write another function named IsSubtreeGreater
[216]
that will return true if all the elements in a subtree
[219]
are greater than the given value. I had just
[223]
declared this functions. I'll write body of these functions later.
[226]
Let's come back to this function IsBinarySearchTree.
[229]
In this function, I am going to say that if all elements in left subtree are
[234]
lesser and I'll verify this by making a call to IsSubtreeLesser function
[238]
passing it
[239]
address of left child of my current root.
[242]
Left child would be the root of current subtree and the data in root.
[247]
This function will return true if all the elements in left subtree
[252]
would be lesser than the data in root. Now the next thing that I want to check
[256]
for is
[257]
if elements in right subtree are greater than the data in root or not.
[262]
These two conditions are not sufficient. We also need to check if left
[266]
and right subtrees are binary search trees are not.
[269]
So I'll add two more conditions here have made a recursive call to
[273]
IsBinarySearchTree function passing it address of left child
[277]
and I have made another call passing address of right child
[280]
and if all these four function call IsSubtreeLesser, IsSubtreeGreater
[285]
and IsBinarySearchTree for left and right subtrees
[289]
return true if all these four checks pass then
[293]
our tree is a binary search tree. We can return true
[297]
else we need to return false. There is only one thing that the a missing in
[301]
this function now.
[303]
We are missing the base case. If the route is null that is
[306]
if the tree or subtree is empty, we can return true.
[310]
This is the base case for our recursion where we should stop.
[314]
With this much of code IsBinarySearchTree function is complete
[318]
but let's also write IsSubtreeLesser and ItsSubtreeGreater
[321]
functions because they are also part of our logic.
[325]
This function has to be a generic function that should check
[328]
if all the elements in a given tree are lesser than
[332]
a given value or not. We will have to traverse the complete
[336]
tree or subtree and see value in all nodes and compare
[340]
these values against this given integer. I'll first handle the base case in
[345]
this function.
[346]
If the tree is empty, we can return true else we need to check if the data in
[350]
root
[351]
is less than or equal to the given value and we also need to recursively check if
[356]
left and right subtrees of the current root have
[359]
lesser value or not. So I'm adding two more conditions here.
[363]
I'm making two recursive calls one for the left subtree and
[367]
another for the right subtree. If all these three conditions are true
[372]
then we are good else we can return false. IsSubtreeGreater function will
[376]
be very similar.
[377]
Instead of writing these two functions IsSubtreeLesser
[380]
and IsSubtreeGreater, we could also do something like this.
[385]
We could find the maximum left subtree
[389]
and compared it with the data in root, if maximum of a subtree is lesser
[394]
then all the elements a lesser and similarly if the minimum of
[398]
a subtree is greater all the elements had greater.
[401]
For the right subtree, we could find a minimum. So instead of writing these two
[405]
functions IsSubtreeLesser
[407]
and IsSubtreeGreater, we could write something like find max
[410]
and find min and this would also fit.
[413]
So this is a solution using one of the approaches.
[416]
Let's quickly run this code on an example binary tree and see how it will execute.
[421]
I have drawn a very simple binary tree here which actually is a binary search
[425]
tree.
[427]
let's assume some addresses for these nodes the tree.
[430]
Let's say the root node is that address 200 and
[433]
I'll assume some random addresses for other nodes as well.
[439]
To check if this binary tree is a binary search tree or not,
[443]
we will make a call to IsBinarySearchTree function.
[447]
I'm writing IBST here as Shortcut for IsBinarySearchTree
[451]
because I'm short of space here. So I'll make a call to this function
[455]
maybe from the main function passing addressed 200,
[459]
address of the root node. For this function call
[462]
address in this local variable address collected in this local variable root
[467]
will be 200. Root is not null.
[470]
Null is only a macro for address 0.
[473]
For this call root is not null, so we will not return true at this line.
[478]
We will go to the next if. Now here, we will make a call to
[482]
IsSubtreeLesser function. Arguments passed will be address
[486]
of left child which is 150 and
[489]
7 the data in node at 200.
[492]
Execution of the calling function will pause and
[496]
will resume only after the called function returns.
[499]
Now in this call to IsSubtreeLesser, root is not null so
[503]
we will not return true at first line. We will go to the next
[507]
if. Now here the first condition is if
[510]
data in root this time
[513]
is 150 because on this call is for this left subtree
[518]
and for this left subtree address of root is 150.
[521]
Data in root is 4 which is lesser than
[526]
7, so the first condition is true and we can go to the second condition which is
[531]
a recursive call.
[532]
This call will pause and we will go to the next call.
[536]
Here once again the data in node
[540]
at 180, 1 is lesser than 7
[543]
so first condition is true and we will make
[547]
recursive call. Left subtree for node at 180
[550]
is null. There is no left child so
[553]
we will return at first line. Root is null this time.
[557]
This particular call will simply to return true. Now in this previous call when
[560]
root is 180,
[562]
second condition for if is also true. So
[565]
we will make another call for right subtree. Once again address passed
[569]
will be 0 and we will simply return true and now for this call
[573]
IsSubtreeLesser 187, all three conditions are true.
[578]
So this guy can also return true and now ISL 150,7 will resume.
[583]
Now this guy will make a recursive call for the right subtree
[587]
and this guy after everything will also return true.
[591]
Now for this call because all 3 conditions in the if statement are true,
[596]
this guy will also return true and now IsBinarySearchTree function will
[600]
resume.
[602]
For this call we have evaluated the first condition
[605]
we have got true now this guy will make another call
[608]
to IsSubtreeGreater, passing address of right child and value 7.
[614]
This guy after everything will return true and now we will have 2 recursive
[619]
calls,
[620]
to check if left and right subtree are binary search trees on not.
[624]
We will first have a call for the left subtree.
[628]
The execution will go on like this but I want you to see something.
[632]
Each call to binary search tree function,
[635]
we are comparing the data in root with all the elements in left subtree and
[639]
then all the elements in right subtree.
[641]
This example tree could be really large then in that case
[646]
in the first call IsBinarySearchTree for this complete tree,
[650]
we would recursively traverse this whole left subtree
[654]
to see whether all the values in this subtree
[657]
are less than 7 or not and then we will traverse all nodes in this right subtree
[662]
to see if values have greater than 7 or not and then
[665]
in next call IsBinarySearchTree, when we would be validating with this
[670]
particular subtree is BST or not.
[672]
We would recursively traverse this subtree if values are lesser than 4
[677]
or not
[677]
and this subtree to see if value so greater than 4 or not.
[681]
So all in all during this whole process there will be a lot of traversal.
[685]
Data in nodes will be read and compared multiple times.
[689]
If you can see all nodes in this particular subtree
[693]
will be traversed once in call to IsBinarySearchTree for 200.
[697]
When we will compare value in these nodes with 7
[701]
and then these nodes will once again be traversed
[705]
in call to IsBinarySearchTree for 150 when
[708]
they will be compared with 4. They will be traversed in call to
[713]
IsSubtreeLesser.
[714]
All in all these two functions IsSubtreeLesser
[717]
and IsSubtreeGreater very expensive. For each node, we are looking at all nodes in
[722]
its subtrees.
[723]
There is an efficient solution in which we do not need to compare
[727]
data in a node with data in all nodes in its
[730]
subtrees and let's see what the solution is.
[733]
What we can do is we can define a permissible
[736]
range for each node and data in that node must be in that range
[741]
we can start at the root node with a range
[744]
-infinity to infinity, because for the root node
[747]
there is no upper and lower limit and now as we are traversing we can set a range
[753]
for other nodes.
[754]
When we are going left, we need to reset the upper bound
[757]
so for this node at 150, data has to be between
[761]
-infinity and seven. Data in left child cannot be greater than data in
[766]
root.
[767]
If we're going right, we need to set the lower bound
[770]
for this node at 300 range would be 7 to infinity.
[775]
7 is not included in the range. Data has to be strictly greater than 7.
[780]
For this node at 180, the range will be -infinity to 4.
[784]
For this node with value 6 lower bond will be 4 and upperbound would be 7.
[790]
Now my code will go like this. My function IsBinarySearchTree will take
[795]
two more arguements,
[796]
an integer to mark lower bound or min value
[800]
and another integer to mark the upper bound or max value
[803]
and now instead of checking whether all the elements in left subtree are lesser than
[807]
the data in root
[808]
and all the elements in right subtree are greater than the date in root or not.
[813]
We will simply check whetheer data in root is
[816]
in this range or not. So I'll get rid of these two function call
[820]
IsSubtreeGreater
[821]
and IsSubtreeGreater which are really expensive and I'll
[824]
add these two conditions. Data in root must be greater than min value
[829]
and data in root must be less than max value. These two checks will take
[833]
constant time.
[834]
IsSubtreeLesser and IsSubtreeGreater functions were not taking
[837]
constant time.
[838]
Running time for them was proportional to number of nodes in the subtree.
[842]
Okay now these two recursive calls should also have two more arguements.
[847]
For the left child lower bound will not change,
[850]
upper bound will be to data in current node and for the right child, upper
[855]
bound will not change
[856]
and lower bond will be data in current node. This recursion looks good to me. We
[861]
already have to base case written.
[863]
The only thing is that the Caller of this IsBinarySearchTree function
[868]
may only want to pass the address of root node so what we can do is instead of
[872]
naming this function IsBinarySearchTree. We can name this function
[876]
as a utility function like IsBstUtil
[880]
and we can have another function name IsBinarySearchTree
[884]
in which we can take only to address of root node and this function can call
[889]
IsBstUtil to function passing address of root.
[893]
Minimum possible value in integer variable for -infinity
[896]
and maximum possible value in integer valuable for
[900]
+infinity INT_MIN and INT_MAX here are macros
[904]
for a minimum and maximum possible values in Int.
[907]
So this is a solution using second approach which is quite efficient.
[912]
In this recursion will go to each node once and at each node we will take
[916]
constant time
[917]
to see whether the data at node is in a defined range or not
[921]
and time complexity would be O(N) where N is number of nodes in the
[925]
binary tree.
[926]
For the previous algorithm, time complexity was O(N^2).
[930]
One more thing, in this code I have not handled
[933]
the case that Binary search tree can have duplicates.
[937]
I am saying that elements in left subtree must be strictly lesser and elements in
[942]
right subtree
[943]
must be strictly greater. I leave it for you to see how you will
[946]
allow duplicates. There is another solution to this problem.
[950]
You can perform in order traversal of binary tree
[954]
and if the tree is binary search tree you would read the data in
[957]
sorted order. In-order traversal of a
[961]
binary search tree gives a sorted list. You can do some hack
[965]
while performing in order traversal and check if you're getting the elements in
[968]
sorted order or not.
[970]
During the whole traversal you only need to keep track of
[973]
previously read node and at any time data in a node that you're reading
[977]
must be greater than the data in previously read node.
[980]
Try implementing this solution, it will be interesting. Okay I'll stop here now.
[984]
In cominng lessons, We will discuss some more problems on Binary tree.
[988]
Thanks for watching.