33

Today I had an interview where I was asked to write a program which takes a Binary Tree and returns true if it is also a Binary Search Tree otherwise false.

My Approach1: Perform an in-order traversal and store the elements in O(n) time. Now scan through the array/list of elements and check if element at ith index is greater than element at (i+1)th index. If such a condition is encountered, return false and break out of the loop. (This takes O(n) time). At the end return true.

But this gentleman wanted me to provide an efficient solution. I tried but I was unsuccessful, because to find if it is a BST I have to check each node.

Moreover he was pointing me to think over recursion. My Approach 2: A BT is a BST if for any node N N->left is < N and N->right > N , and the in-order successor of left node of N is less than N and the in-order successor of right node of N is greater than N and the left and right subtrees are BSTs.

But this is going to be complicated and running time doesn't seem to be good. Please help if you know any optimal solution.

alex
  • 479,566
  • 201
  • 878
  • 984
dharam
  • 7,882
  • 15
  • 65
  • 93
  • 3
    An inorder traversal already gives you the values in the order they would appear in the array, so you don't need to copy the whole tree, you just need to keep track of the last value encountered, so it can be compared with the current one. – anton.burger May 31 '12 at 11:31
  • Wow! this is true, I probably dont need the array, even then the order is O(n) – dharam May 31 '12 at 11:35
  • Can you define what your interviewer meant by "efficient"? Did he mean time or space? I tend to agree with you that you can't do it without checking each node, but you don't need the array. – anton.burger May 31 '12 at 11:38
  • He wanted me to optimize this in terms of time. I think it can't be done in less than O(n) – dharam May 31 '12 at 11:42
  • 3
    He wants you to tell him that it *cannot be done* in less than O(n) :-) and if someone claims it can, one can exchange one of the nodes that wasn't checked by a BST-destroying value to show him wrong. (Don't forget that it's fair to ask impossible things in interviews ;) – Frank May 31 '12 at 12:15

7 Answers7

78

It's a pretty well-known problem with the following answer:

public boolean isValid(Node root) {
     return isValidBST(root, Integer.MIN_VALUE,
          Integer.MAX_VALUE);
}
private boolean isValidBST(Node node, int l, int h) {
     if(node == null)
         return true;
     return node.value > l 
         && node.value < h
         && isValidBST(node.left, l, node.value)
         && isValidBST(node.right, node.value, h);
}

The recursive call makes sure that subtree nodes are within the range of its ancestors, which is important. The running time complexity will be O(n) since every node is examined once.

The other solution would be to do an inorder traversal and check if the sequence is sorted, especially since you already know that a binary tree is provided as an input.

Dhruv Gairola
  • 9,102
  • 5
  • 39
  • 43
  • 4
    What your code do against a tree like this https://www.dropbox.com/s/mg7jpqbn9z7jvn1/Screenshot%202014-07-16%2017.41.42.png . Doesnt it evaluate to true?(Even if its not a BST) – seeker Jul 17 '14 at 00:42
  • I just don't get this line Math.min(node.value,MAX), could you please provide more explanation? – moji Nov 13 '15 at 18:07
  • I think Math.min(node.value,MAX) can be simply replaced with node.value – EFreak Nov 17 '15 at 21:28
  • This algorithm does not work correctly for all input values. For instance, if root node has a value of Integer.MIN_VALUE or Integer.MAX_VALUE, algorithm will return false, but it would actually be a perfect BST, and should return true. To correct the algorithm you can replace initial min, max values with null and check against nullability. This way your algorithm will work for all input values. – CanC Dec 14 '15 at 11:14
  • the expression inside your if statement is a boolean itself. just return that. – koolaang Jul 24 '17 at 20:25
  • @KodeSeeker The tree you have referenced is a BST though. – Dhruv Gairola Sep 02 '17 at 19:53
  • @DhruvGairola But look at the number 60, it's bigger than the root note so it should be in the right subtree. Should it not? – aledujke Nov 30 '17 at 09:44
7

The answer provided by @Dhruv is a good one. In addition to that here is one another solution that works in O(n) time.
We need to keep track of the previous node in this approach. In each recursive call we check the previous node data with the current node data. If current node data is less than previous we return false

int isBST(node* root) {
  static node* prev  = NULL;

  if(root==NULL)
    return 1;

  if(!isBST(root->left))
    return 0;

  if(prev!=NULL && root->data<=prev->data)
    return 0;

  prev = root;
  return isBST(root->right);
}

Richard
  • 56,349
  • 34
  • 180
  • 251
AgentX
  • 1,402
  • 3
  • 23
  • 38
  • 1
    `prev!=NULL && root->data<=prev->data` ...incorrect....if the 2nd left child is greater than parent, this algo would fail in that case! – NoobEditor Jun 05 '14 at 07:17
  • @NoobEditor I don't think so. Since it is scanning the nodes on in order fashion if any left child is greater than its parent it will return 0 at that moment. So if the 2nd left child is greater than parent node than the 1st left child of the parent would also have been greater than it otherwise the function would have returned a 0 value. If you can think of some case where the function fails kindly provide it. – AgentX Jun 06 '14 at 08:28
  • you code only checks for immediate childs...not subchilds.As Breadth first traversal, take this scenario `[10 8 15 5 12]` – NoobEditor Jun 06 '14 at 09:24
  • I tried it with root = 10, root->left = 8, root->right=15, root->left->left = 5, root->left->right = 12 and it is working correcty! – AgentX Jun 21 '14 at 11:14
1
boolean b = new Sample().isBinarySearchTree(n1, Integer.MIN_VALUE, Integer.MAX_VALUE);
.......
.......
.......
public boolean isBinarySearchTree(TreeNode node, int min, int max){
  if(node == null){
    return true;
  }

  boolean left  = isBinarySearchTree(node.getLeft(), min, node.getValue());
  boolean right = isBinarySearchTree(node.getRight(), node.getValue(), max);

  return left && right && (node.getValue()<max) && (node.getValue()>=min);      
}

Comments are invited. Thanks.

Richard
  • 56,349
  • 34
  • 180
  • 251
Trying
  • 14,004
  • 9
  • 70
  • 110
0

I think that the second approach is right. The tree can be traversed in a recursive manner. On each iteration lower and upper bounds of current subtree can be stored. If we want to check subtree with root x, and bounds for the subtree are l and h, then all we need is to check that l <= x <= h and to check the left subtree with bounds l and x, and the right one with bounds x and h.

This will have O(n) complexity, because we start from the root and each node is checked only once as root of some subtree. Also, we need O(h) memory for recursive calls, where h is the height of the tree.

fdermishin
  • 3,519
  • 3
  • 24
  • 45
  • Can you please elaborate and explain the Running time complexity of this approach. According to me its greater than O(n). But I am not sure. – dharam May 31 '12 at 11:27
  • So, according to you this is still going to take O(n). I was asked to optimize it. – dharam May 31 '12 at 11:37
-1

There are some examples above using INTEGER.MAX AND MIN I cant see a reason to pass them and the significance of it, correct me if I am wrong in anyway or explain me the reason.

More over binary search tree may have objects which are compared by compareTo method or Coperator.. ( hence Integer.MIN and Integer.MAX dont fit on that model) I am writing a code where it returns true or false one has to call (root_node,true) and it will return true if it is a bst else false

void boolean isBSt( node root_node, boolean passed_root)
{

  if ( node==null){
        if ( passed_root)
        return false;
        // you have passed null pointer as 
        //root of the tree , since there is no 
        // valid start point we can throw an exception or return false      
        return true;
   }

  if( node.right !=null ) 
     if ( node.right.data <= node.data)
   return false;

  if ( node.left !=null ) 
        if ! ( node.left.data <= node.data) 
    return false;

  return ( isBST( node.right , false) && isBST( node.left, false ) )

}
user2398113
  • 65
  • 1
  • 2
  • The MIN and MAX is just to avoid a corner case and make that recursive call look symmetric. – dharam May 21 '13 at 08:59
  • yaa I understood.. why it is like that.. – user2398113 May 22 '13 at 07:40
  • The MIN and MAX values bound the verification at lower levels of the tree with the values of ancestors. Your function does not know the values of its ancestors and thus may incorrectly mark trees as valid. – Richard May 28 '14 at 14:05
-1

Have a look at this solution: http://preparefortechinterview.blogspot.com/2013/09/am-i-bst.html

It explains different ways and gives you a generic and efficient method too. Hope it helps.

Furquan
  • 676
  • 3
  • 5
-2

Here is another Solution which uses 2 helper functions to calculate for each node the min and max value in the subtree using the helper function minValue and maxValue

int isBST(struct node* node)
    {
      if (node == NULL)
        return(true); 

      /* false if the max of the left is > than us */
        if (node->left!=NULL && maxValue(node->left) > node->data)
            return(false); 

      /* false if the min of the right is <= than us */
        if (node->right!=NULL && minValue(node->right) < node->data)
            return(false); 

      /* false if, recursively, the left or right is not a BST */ 
         if (!isBST(node->left) || !isBST(node->right))
           return(false); 

      /* passing all that, it's a BST */
          return(true);
    }
Richard
  • 56,349
  • 34
  • 180
  • 251
Sanil
  • 42
  • 4