James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 164 , comments - 1395 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in the Seattle area, who has been performing C++/C#/Java development for over 20 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

Blogs I Read

Follow BlkRabbitCoder on Twitter

Tag Cloud

Archives

Post Categories

.NET

CSharp

Little Wonders

Little Wonders

vNext

Solution: Little Puzzlers–Is tree a Binary Search Tree?

This is the way I would go about the “Is tree a Binary Search Tree” problem if I were asked to perform it at an evaluation. However, keep in mind there are multiple ways to solve this, so don't worry if your solution has variations.

The Wrong Path

The temptation here is to naively think of a BST as simply having the restriction that the left child of x must be < x and the right child of x must be > x.

The problem is that this is not the accurate definition.  The definition of a BST is that the left subtree of x must be < x and the right subtree of x must be > x, which is more complex to check.

           5

        /    \       

      3        8     

    /   \     /      

   2     6   7    

Notice that the tree above satisfies the naïve rule for every child, but not for the whole subtree.  Thus, this is not a BST because even though 6 > 3, it is not < 5 so it cannot be on the left subtree of 5.

My Solution: Diminishing Ranges

One way to tackle this is to traverse the tree keeping track of the valid range of items, and then when you go left or right, simply modify that range accordingly.

Let’s assume int for now, since it’s easy to conceptualize.  And further, let’s assume that int.MinValue < x < int.MaxValue just for simplicity sake.

Thus, when we examine the root in our example above, we will test 5 against the range int.MinValue to int.MaxValue.  5  is indeed in this range, so we are good so far.  Now, we know the left subtree of 5 must be < 5 and the right subtree of 5 must be > 5 so we can recursively call our method on the left subtree with the range int.MinValue to 5  and on the right subtree with the range 5 to int.MaxValue.

As we continue down, this will refine our range as we branch left and right at each node because at each step, we will pass in the min and max for the range, then divide it by the current node.

This gives us the following method (assuming just int where int.MinValue < x < int.MaxValue):

public static bool IsBst(Node<int> root, int min = int.MinValue, int max = int.MaxValue) 
{
    if (root != null)
    {
        return root.Data > min && root.Data < max
            ? IsBst(root.Left, min, root.Data) && IsBst(root.Right, root.Data, max)
            : false;
    }
 
    return true;
}

If you don’t like that this eliminates int.MinValue and int.MaxValue from the range, you could have those be null to designate the ends:

public static bool IsBst(Node<int> root, int? min = null, int? max = null) 
{
    if (root != null)
    {
        return ((!min.HasValue || root.Data > min) && (!max.HasValue || root.Data < max))
            ? IsBst(root.Left, min, root.Data) && IsBst(root.Right, root.Data, max)
            : false;
    }
 
    return true;
}

But we can get even better than that, we could make this method generic so that it can handle any IComparable. 

Here is the version for reference types:

public static bool IsBst<T>(Node<T> root, T min = null, T max = null) where T : class, IComparable<T>
{
    if (root != null)
    {
        return ((min == null || root.Data.CompareTo(min) > 0) && 
                (max == null || root.Data.CompareTo(max) < 0))
            ? IsBst(root.Left, min, root.Data) && IsBst(root.Right, root.Data, max)
            : false;
    }
 
    return true;
}

And for value types (note the need to use nullable types):

public static bool IsBst<T>(Node<T> root, T? min = null, T? max = null) where T : struct, IComparable<T>
{
    if (root != null)
    {
        return ((!min.HasValue || root.Data.CompareTo(min.Value) > 0) &&
                (!max.HasValue || root.Data.CompareTo(max.Value) < 0))
            ? IsBst(root.Left, min, root.Data) && IsBst(root.Right, root.Data, max)
            : false;
    }
 
    return true;
}

Alternative approach: Same thing, but iterative

If you just really hate recursion (aside from the fact you’re using, in essence, a recursive data structure), it is possible to solve this check iteratively. 

To do this, you could simply create a queue and push on a small structure containing the node to check and it’s min and max range.  You would seed this with the root and null for the min and max, then enter a loop.

For as long as the queue is not empty, check the current node against min and max and if out of order, stop processing.  If in order, then push on the  children with their modified ranges much like the recursive example.  Repeat until queue is empty.

 

Alternative approach: In-Order Traversal

As an alternative, you could also perform an in-order traversal on the tree and store each node into a queue, then make a single pass through the queue looking for any item out of place (i.e. any item that is >= the item after it).

 

Summary

Hope you had fun with this one!  Of course, I’m sure many out there can tweak these answers for performance in various ways – but you get the point.

Have a solution that worked for you but was totally different?  I’d love to hear about it!

Stay tuned next week for the next Little Puzzler.

Print | posted on Monday, March 30, 2015 12:22 PM | Filed Under [ My Blog C# Software .NET Little Puzzlers ]

Feedback

Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

There's also the less memory-intensive in-order solution.

You traverse the tree using an in-order traversal, caching the value that you had just come from. If at any point the value you're at is less than the value you just came from, the tree isn't sorted.

You don't have to copy the tree into a list and then check that list for being sorted. You just traverse the tree in that order and make sure it's sorted.
3/30/2015 3:41 PM | Jake
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

@Jake: Very true. If you have an external (as in, not a parameter) variable storing the last output variable, that would work as well.
3/30/2015 3:44 PM | James Michael Hare
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

Jakes solution is prvably the optimal solution, since it only requires O(log(n)) space (maximum height of tree for the stack that the DFS requires) and visiting each node of the tree exactly once. It's clear that each node of the tree must be visited at least once to find all errors, making this the most efficient way to solve that puzzle.
3/31/2015 4:00 AM | Alex
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

@Alex: actually my first soution is also O(log n) stack space and only visits the tree nodes at most once. And the second statement is not quite correct, you need not visit each node at least once if you find an out-of-order node early on, in which case you can immediately sort-cut and return false.
3/31/2015 7:30 AM | James Michael Hare
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

@James: the solution I posted before (http://pastebin.com/1gV8zjcZ) uses a closure to capture the "external" variable - putting the entire implementation into a single method without the need for a variable external to the parent method.
3/31/2015 6:55 PM | nelson
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

@nelson: yep, I like your solution. I think with the lambda expression inside you escape the single method definition on a technicality :-)

Of course, one could do something similar with a primer method that starts at the root and passes in a temp variable by ref into the helper method which modifies the ref variable with each node it passes in sequence.

Either way, all valid solutions and ways to hide the state external to the traversal.
3/31/2015 8:23 PM | James Michael Hare
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

You dont need a queue for an interative version. Just convert the call to IsBst in the tail call position into a while loop.
4/1/2015 5:03 AM | leppie
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

Typo: iterative

Also, I did not note the &&, so my while approach will not work completely, just halfway ;p
4/1/2015 5:04 AM | leppie
Gravatar

# re: Solution: Little Puzzlers–Is tree a Binary Search Tree?

No worries @leppie :-)
4/1/2015 8:59 AM | James Hare
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: