Amazon Interview Question for Software Engineer / Developers


Country: United States




Comment hidden because of low score. Click to expand.
3
of 3 vote

Solution :
step 1 :

- find the first node whose value is lies between [a,b], that node can be root node also
 - Once you have found it, start inorder traveling from that node, and print accordingly.

- sonesh February 23, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Though this is gives a right result,it is not an efficient approach(i.e takes O(N) ).Use Divide and Conquer Solution for getting a result in O(logN)

- zuzu1985sharma February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@zuzu : The tree is BST, so searching a node require only O(log n) time, now once you have found that node, you need to find all the element. which require O(# element which lies in [a,b])

- sonesh February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@sonesh, Have you thought about how you are going to do an inorder traversal starting from the middle of the tree? Are you going to have parent links or maintain kind of stack? Try coding this and I bet you'll see why this is a much better solution:

if tree is null, you're done
if min <= root, traverse left
if min <= root <= max, output root
if root <= max, traverse right

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@showell: Sonesh has interpreted the question to get _all_ the nodes and not just _any_ node, so outputting just the root is not enough.

Good thing about this solution is it is composed of well known operations, Find and Successor, which will likely exist in implementations which support ordered iteration, and can be shown to be O(h + S) where h is the height of tree and S is the number of elements to be output.

Of course, the interviewer might ask you to implement those and might be looking for something simpler, in which case a modified version of showell's solution will work.

- Anonymous February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Anonymous, the recursive calls to traverse make it so that you output _all_ the nodes. Change "output" to "visit" or "yield" to be more general.

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@showell: Yes, there is no denying we can write the recursive version, but we do need to recurse both left and right in some cases, and not just one subtree (which you seemed to be implying).

The point is, sonesh's solution is not as bad as your comments seemed to imply (not claiming you actually did, just what it seemed). It is quite reasonable. In fact, in production code, that will probably be the one to use: simplicity and ease of maintenance etc, especially if the off-the-shelf supports ordered iteration.

- Anonymous February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Here is a working example of code that doesn't require any special operations from a binary tree other than "right" and "left."

def test():
    lst = [1,2,3,4,4,5,6,7,7,7,8,9,10,11,12,13,14,15]
    tree = create_tree(lst)
    assert [4,4,5,6,7,7,7,8,9,10,11,12] == list(traverse_range(tree, 4, 12))
    assert [] == list(traverse_range(tree, 16, 100))
    assert lst == list(traverse_range(tree, 0, 100))

def traverse_range(tree, min, max):
    def traverse(t):
        if t is None:
            return
        left, root_val, right = t
        if min <= root_val:
            for v in traverse(left):
                yield v
        if min <= root_val <= max:
            yield root_val
        if root_val <= max:
            for v in traverse(right):
                yield v
    for item in traverse(tree):
        yield item

# In Python a simple way to represent a binary
# tree is as a tuple: (left_tree, val, right_tree)
def create_tree(lst):
    def _maketree(low, high):
        # Making a balanced tree is simply a matter
        # of taking the middle value as the root
        # and recursively building the subtrees
        # on the left and right.
        if low > high:
            return None
        if low == high:
            return (None, lst[low], None)
        mid = (low + high) / 2
        val = lst[mid]
        left = _maketree(low, mid - 1)
        right = _maketree(mid + 1, high)
        return (left, val, right)
    return _maketree(0, len(lst) - 1)

test()

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Anonymous, of course there are occasions where you traverse both subtrees. Nothing in my answer implied otherwise. If @sonesh's approach is easy, then somebody should post the code.

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@showell, Your using yield like that could make it potentially Omega(h(h+s)), and not O(h + S) (i.e. an extra multiplicative factor of the height).

As for the "easy" code, assuming there is a find and successor implemented by the underlying tree, it could be as easy as

/// C++ like code.
RangeVisit(const Tree<T> &tree, T a, T b, Visitor &v) {
    auto it = tree.find(a); // finds element of tree >= a
    while (it != tree.end() && *it <= b) {
        v.Visit(*it);
        it++; // Successor
    }
}

The above code is probably much more easier to debug/maintain that a custom recursive implementation.

Of course, for an interview the interviewer would probably say successor is not available, as they actually want you to write some "reasonable amount" of code, which they can judge. Or perhaps their next question would be to discuss how such an iterator/successor/find can be implemented... Of course, depends on the interviewer.

Debating which solution is better is quite pointless without the context.

- Anonymous February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

There is no multiplicative factor for height in my algorithm. Even in the worst-case scenario, where the tree is completely vertical, it visits every node once, for a running time of 2H. I have no idea where you're getting the H-squared from.

People often implement BSTs without parent pointers to save space and maintenance overhead. Without parent pointers, it's tough to write a successor function. With parent pointers and/or some other storage scheme that allows for a reasonable successor function, I see the merits of your solution.

- showell30@yahoo.com February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@showell: Think about how yield is actually implemented by the compiler/interpreter.

Perhaps it is not applicable to python (hence the usage of the word "potentially"), but the C# version would have problems. Read this:

stackoverflow. com/questions/3969963/when-not-to-use-yield-return

See the answer by Eric Lippert.

btw, you don't need parent pointers to implement successor. Of course you would need some auxiliary storage like a stack, but that can be made O(h), just like your recursive version.

- Anonymous February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Ok, I think I see what you mean. Let's say you have a pretty deep tree, say 1M elements and about 20 deep. All the elements in the bottom layer require 20 touches before they're finally yielded. Python does have a "yield from" in recent versions, I believe, that prevents this problem. In earlier versions, I would just change the code to pass in a visitor function to collect the elements.

- showell30@yahoo.com February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

There's a PEP called "Syntax for Delegating to a Subgenerator" that's been accepted in Python, but I'm not sure if they actually optimise for the delegated-yield issue.

- showell30@yahoo.com February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@showell30 : I think you guys have wrote enough already, but I just wanna say that First : to find a node in BST does not require a stack, secondly once we found such node, consider that node as root node and apply our inorder algorithm.(Inorder algorithm only require a node and nothing else about tree)

- sonesh February 25, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@sonesh. Yes to find the first node we don't need a stack. But you are mistaken about rooting the tree at the node found. I suggest you rethink.

(Of course, there are tree versions like threaded trees where you don't need a stack or extra space to be able to find the successor...)

- Anonymous February 25, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@sonesh: Consider the tree 1 <- 2 -> 3. If you want to find numbers in the range 1 to 3 (inclusive), then your algorithm will first find 1 as the root. An inorder traversal with 1 as the root won't get you back to 2 and 3, unless you have parent pointers or a stack. It's really that simple.

- showell30@yahoo.com February 25, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@showell :
If tree is

2
  /\
1 3

and i need to find nodes between 1 and 3, then the root node which is 2 is the node from where we start in order algo.
if tree is

6
       /    \
     4     8
   /   \   /   \
 2   5  7   10

Now in this question if the range is [1 5] then at root node which 6 in not belong to [1,5] and is greater then 5, so we go left, Now 4 is in [1,5], so we consider 4 as root and start inorder traveling
How ?
Here is how, If we consider 4 as root, then the new tree is

4
 /  \
2  5

so we can see that all 2,4,5 are inside [1,5].

- sonesh March 03, 2013 | Flag
Comment hidden because of low score. Click to expand.
2
of 6 vote

Divide and Conquer Solution:

If the tree is empty, return not found. If min <= root <= max, then return root. If root < min, recursively search the right subtree. Otherwise, recursively search the left subtree.

Assuming the tree is balanced, this runs in O(log N) time, vs. O(N) time for an inorder traversal.

- showell30@yahoo.com February 23, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

This is right.

- Anonymous February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Of course, it is assuming height of the tree is O(log N), but anyway...

- Anonymous February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

It's reasonable to assume that the interviewer is talking about a balanced binary tree, even though the question doesn't explicitly state it. Even for a horribly imbalanced tree, this approach is better than a naive inorder traversal.

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Interviewers actually don't take kindly to unnecessary assumptions... just say O(h) instead of O(log N), then discuss that random binary trees have expect h = O(log N) etc.

You don't have to assume balanced...

- Anonymous February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

I've edited my answer to say "Assuming the tree is balanced."

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

This is not correct answer.
suppose BST contains numbers from 20 to 30. Suppose [min, max] = [19, 31].
1. first problem is that it will return from the root itself.
2. your solution will go to the else part and recursively search the left subtree.

Instead of printing the whole tree, your solution will print only the left sub tree from the tree root.

Down voted !!

- Vinod February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

I agree with Vinod...
It actually doesn't matter if the tree is BST or not. If min value is less than the least value of the BST and max value if greater than the greatest value in the BST, all the nodes in the BST would have to be visited. Hence, the worst case running time should be O(n)...

- Rahul Arakeri February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Vinod, Read the awkwardly worded question again and you'll see that's it's a valid interpretation to find just one element in the range. It says "print the node," which suggests you're just trying to find one match.

@Rahul, The long thread on this page that starts with "find the first one" has a detailed discussion of the running times for two different implementations, using the interpretation that you have to report all values in the range, not just one.

- showell30@yahoo.com February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
1
of 1 vote

Python Iterative Search.

This code avoids recursion by maintaining its own stack. It's an inorder traversal of the tree, but it only yields elements that are within range, and it short circuits traversing the parts of the tree that are out of range, as well as short circuiting stack pushes for parts of the tree that are more than max.

def traverse_range(tree, min, max):
    stack = []
    while tree or stack:
        if tree:
            left, val, right = tree
            if val <= max:
                stack.append(tree)
            tree = left
            if val < min:
                tree = None
        else:
            tree = stack.pop()
            left, val, right = tree
            if val > max:
                return
            if min <= val:
                yield val
            tree = right

- showell30@yahoo.com February 24, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Nice!

- Anonymous February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

Since its a BST, therefore inorder traversal will give traversal in sorted order, therefore do not print the first and the last elements.

- DashDash February 23, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I have done simple in-order traversal with additional return condition.

bst_print_range(root r, int lo , int max)
{
	if(r==NULL || r->data < lo || r-> data > max)
		return ;
	bst_print_range(r->left,lo,max);
	print("%d",r->data);	
	bst_print_range(r->right,lo,max);
}

- Aditya February 23, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

The problem with inorder traversal is that it's O(N). See "Divide and Conquer Solution" for a simple approach that is much more efficient for large trees.

- showell30@yahoo.com February 23, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

1. find the node which is in the range [min,max] using binary search
2. then do normal inorder traversal from this node the break condition to be
if(node == null || node.data < min || node.data > max)
return;

- Anonymous February 23, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

The question is poorly written. It's not clear whether the goal is to enumerate all the nodes in the range or simply find any matching node.

- showell30@yahoo.com February 23, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Can also perform bfs between nodes 10 and 17 and display only values that lie between 10 and 17.

- Swet February 24, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

C++ lazy iterator using stack. Since C++ does not have yield, it is trickier to provide the lazy iteration. (note, quick proof-of-concept code, so not exemplary and probably buggy).

The stack invariant is that the from bottom to top (along the stack), the nodes are from the path starting at the root to the top node (in the tree), but only those nodes which are of value >= the value of the top node.

Even though the code looks verbose, the core are moveToFirst and next, which are quite simple to code up, actually.

#include "tree.h" // Tree definition.
#include <stack>
  
#include <iostream>
  
using std::stack;
  
// Won't bother with making this generic.
// implements JAVA style iterator hasNext and next.
class BSTRangeIterator {
public:
	BSTRangeIterator(Tree *t, int l, int u) {
		_tree = t;
		_lower = l;
		_upper = u;
  
		if (_tree == NULL) {/*throw*/;}
		if (_upper < _lower) {/*throw*/;}
  
		moveToFirst();
	}
  
	bool hasNext() {
		if (_stack.empty()) return false;
		Tree *next = pop();
		if (next->Value > _upper) {
			return false;
		}
		push(next);
		return true;
	}
  
	int next() {
		if (!hasNext()) {
			// throw
		}
  
		Tree *top = pop();
		Tree *right = top->Right;
		int val = top->Value;
		while (right != NULL) {
			if (right->Value <= _upper) {
				push(right);
			}
			right = right->Left;	
		}
		return val;
	}
  
private: 
	// Moves to smallest element >= _lower.
	// Stack contains path to the required element
	// Only elements >= _lower are on the stack.
	void moveToFirst() {
		push(_tree);
		while (!_stack.empty()) {
			Tree *cur = pop();
			if (cur == NULL) {
				return;
			}
			if (_lower > cur->Value) {
				push(cur->Right);
			} else {
				push(cur);
				push(cur->Left);
			}
		}
	}
  	
	void push(Tree *t) {
		_stack.push(t);
	}
  
	Tree *pop() {
		Tree *top = _stack.top();
		_stack.pop();
		return top;
	}
  
	Tree *_tree;
	int _lower;
	int _upper;
  
	stack <Tree *> _stack;
};

void Test(Tree *t, int l, int u, int start,  const char *message) {
	std::cout << message << ":";
	BSTRangeIterator iter(t, l,u);
	int expected = start;
	bool failed = false;
	while(iter.hasNext()){
		int cur = iter.next();
		if (cur != expected) {
			failed = true;
			break;
		}
		expected++;
		std::cout << " " << cur << ",";
	}
	if (failed) {
		std::cout << " Failed\n";
	} else {
		std::cout << " Passed\n";
	}
}
  
int main(int argc , char **argv) {
	int values[] = {15,1,4,8,7,2,3,6,9,10,11,12,13,14,5,16,17,18,19,20};
	int len = sizeof(values)/sizeof(int);
  
	Tree *t = NULL;
	for (int i = 0; i < len; i++) {
		if (t == NULL) {
			t = new Tree(values[i]);
		}else {
			t->Insert(values[i]);
		}
	}
  
	Test(t, 5, 14,5, "Left Only");
	Test(t, 16, 20,16, "Right Only");
	Test(t, -1,15,1, "Left incl root");
	Test(t, 15,111,15, "Right incl root");
	Test(t, -1, 1000, 1, "All");
}

- Anonymous February 24, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Missing check for equality in moveToFirst.

- Anonymous February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

void print_all ( BSTNode root, int min, int max ) {
	
	if root is null return;

	if ( root. data >= min && root.data<=max ) {
		
		print  root.data
		
		print_all ( root.left, min, max );
		print_all (root.right,min,max );

			
	
	}

}

- unsigned_int February 24, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

class TreeNode {
    int val;
    TreeNode leftchild;
    TreeNode rightchild;
    
    public TreeNode(int val) {
	this.val = val;
	leftchild = null;
	rightchild = null;
    }
}


public class Solution {
    public static ArrayList<Integer> search(TreeNode root, int max, int min) {
	ArrayList<Integer> ret = new ArrayList<Integer>();
	if (root == null || max < min) {
	    return ret;
	}
	traverse(root, max, min, ret);
	// binarySearch(root, max, min, ret);
	return ret;
    }

    private static void traverse(TreeNode root, int max, int min, ArrayList<Integer> ret) {
	if (root == null) return;
	traverse(root.leftchild, max, min, ret);
	if (root.val>=min && root.val<=max) {
	    ret.add(root.val);
	}
	traverse(root.rightchild, max, min, ret);
    }

    private static void binarySearch(TreeNode root, int max, int min, ArrayList<Integer> ret) {
	if (root == null) return;
	if (root.val < min) binarySearch(root.rightchild, max, min, ret);
	else if (root.val > max) binarySearch(root.leftchild, max, min, ret);
	else {
	    ret.add(root.val);
	    binarySearch(root.rightchild, max, min, ret);
	    binarySearch(root.leftchild, max, min, ret);
	}
    }
}

- duo.xu.1@stonybrook.edu February 25, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Inorder traversal with min and max element as input

public void printBetweenNodes(Tree node , int min , int max)
{
if(node == null)
return;
printBetweenNode(node.left,min,max);
if(node.data >= min && node.data <= max)
syso(node.data);
printBetweenNode(node.right,min,max);
}

- Mr.karthik.p February 25, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Print left first, then mine, then right so order is preserved in printing.

void printRange( TreeNode *root, int min, int max ) {
	if ( root == NULL ) return;
	if ( root->data > min ) {
		printRange( root->left, min, max );
	}  
	if ( ( root->data > min ) && (root->data < max) ) {
		std::cout << root->data << ",";
	}
	if (root->data < max) {
		printRange( root->right, min, max);
	}
}

- bunnybare February 25, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

It doesn't work if (min & max ) < root->data || > root->data
Here range must be min < root < max like this.
I think this is not a target solution, Can u improve it.

- TusharPatil February 25, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

I fixed it a little bit. Let me know.

- bunnybare February 25, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

This method prints the first node , whose value lies between i and j

private void findNodeBetweenValues(Node node ,int i, int j) {
		if(i > j){
			throw new IllegalArgumentException();
		}
		if(node.data > i && node.data < j){
			System.out.println("value between "+i+" & "+j+" is "+this.root.data);
		}else if(node.data < i){
			findNodeBetweenValues(node.left,i,j);
		}else if(node.data > j){
			findNodeBetweenValues(node.right,i,j);
		}

}

- goutham467 February 26, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

public Node findNode(Node root, int max, int min){

if(root == null) throw new RuntimeException("Tree root null");

Node currNode;

// doing a DFS approach

Queue<Node> queue = new LinkedList<Node>();

queue.enqueue(root);

while(!queue.isEmpty()){

currNode = queue.dequeue();

if(currNode.data > min && currNode.data< max){

return currNode;

}
else{
if(currNode.left != null && currNode.data > max)

queue.enqueue(currNode.left);

if(currNode.right != null && currNode.data < min)

queue.enqueue(currNode.right);
}

}

return null;

}

- Anup February 28, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

void PrintRange(struct tNode *root, int min, int max){
	if (root == NULL)
	return;
	else if (min <= root->data && root->data <= max) {
		PrintRange(root->left, min, max);
		printf("root->data: %d \t", root->data);
		PrintRange(root->right, min, max);
	}
	else if (root->data < min)
	{
		if (root->right)
			PrintRange(root->right, min, max);
		else
			printf("no element in range \n");
	}
	else if (root->data > max) {
		if (root->left)
			PrintRange(root->left, min, max);
		else
			printf("no element in range \n");
	}
}

- flipper March 02, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Inorder traversal ..return element when we found min and we have max after 2 element then return next after min..otherwise return all from min to max

- abhi March 09, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
-1
of 1 vote

Question can be interpeted as LCS (Longest common ancestor ) .so igf we found lcs we have to print the path from lcs node to min and max node

- Raj February 25, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Huh?

- Anonymous February 25, 2013 | Flag


Add a Comment
Name:

Writing Code? Surround your code with {{{ and }}} to preserve whitespace.

Books

is a comprehensive book on getting a job at a top tech company, while focuses on dev interviews and does this for PMs.

Learn More

Videos

CareerCup's interview videos give you a real-life look at technical interviews. In these unscripted videos, watch how other candidates handle tough questions and how the interviewer thinks about their performance.

Learn More

Resume Review

Most engineers make critical mistakes on their resumes -- we can fix your resume with our custom resume review service. And, we use fellow engineers as our resume reviewers, so you can be sure that we "get" what you're saying.

Learn More

Mock Interviews

Our Mock Interviews will be conducted "in character" just like a real interview, and can focus on whatever topics you want. All our interviewers have worked for Microsoft, Google or Amazon, you know you'll get a true-to-life experience.

Learn More