Need help understanding inorder successor in binary search tree - algorithm

I need help in understanding this interview question:
Q: Find an algorithm to find the next node (e.g., inorder successor) of a given node in a binary search tree where each node has a link to its parent.
Does parent mean the in-order predecessor or just the immediate parent? How would one create a tree where the nodes have a link to the root node or inorder predecessor? Any help in understanding the data structure and the program below would be appreciated...
the solution (As posted in a form) is shown below:
public static TreeNode inorderSucc(TreeNode e) {
if (e != null) {
TreeNode p;
// Found right children -> return 1st inorder node on right
if (e.parent == null || e.right != null) {
p = leftMostChild(e.right);
} else {
// Go up until we’re on left instead of right (case 2b)
while ((p = e.parent) != null) {
if (p.left == e) {
break;
}
e = p;
}
}
return p;
}
return null;
}
public static TreeNode leftMostChild(TreeNode e) {
if (e == null) return null;
while (e.left != null) e = e.left;
return e;
}

When nodes store pointers to their parents, it almost always maeans their immediate parents and not their successors. The question in this interview is then how to navigate the tree to find the inorder successor.
The way to find an in order successor is to think about what would happen if you were to recursively do an in order walk of the tree, then to mimic what would happen next. In particular, the logic for an in order traversal always visits all of a node's left subtree, then the node itself, and then the right subtree. To find the in order successor, you need to see what case you're in.
If the node you're currently at has a right subtree, then the in order successor of that tree must be the very first node that would be visited in that subtree during an in order traversal, which is the smallest element of that tree. You can find this by descending into the right subtree, then marching down the left spine of the tree until you find the leftmost node.
If the node does not have a right subtree, then it's the largest node in some subtree. If you think about how the recursion works, the next step in an in order traversal would be to have all the recursive calls that just finished expanding their right subtrees return. Once this last frame returns, you'll visit the node of the first tree that expanded just its left subtree. Consequently, the in order successor of the node can be found by walking up the tree until you reach a node that's a left child. The parent of this node is then your successor. Alternatively, as an edge case, if you hit the root of the tree, that would also be your successor.
Hope this helps!

Related

Time complexity of finding k successors in BST

Given a binary search tree (BST) of height h, it would take O(k+h) time to apply the BST InOrder Successor algorithm k times in succession, starting in any node, applying each next call on the node that was returned by the previous call.
Pseudo code:
get_kth_successor(node):
for times = 1 to k:
node = successor(node)
return node
How can I prove this time complexity?
In particular, I am trying to establish a relation between k and the number of nodes visited, but can't find any pattern here.
Take the following truths concerning a successor(s) traversal:
You can traverse a branch not more than two times: downward and upward.
Every double visit of a branch corresponds to finding at least one more successor: when you backtrack via a branch upwards, you will have visited at least one successor more than at the time you passed that same branch the first time, in the downward direction.
The number of branches you will traverse only once cannot be more than 2h. This worst case happens when you start at a leaf in the bottom-left side of the tree and must go all the way up to the root (a successor) and then down again to a bottom-leaf to find the successor of the root. But if you need more successors after that, you will have to visit some of these branches again (in backtracking) before you can traverse other branches for the first time. So the total number of branches you traverse only once cannot increase above 2h.
So, to find k successors you will at the most traverse k branches twice (downward and upward, cf. point 2) and 2h branches once (point 3), which comes down to a worst case branch-traversal-count of 2k+2h which is O(h+k).
I am going to write the full implementation for the problem in order to make it easy to prove my arguments about the time being taken.
.
Assuming that each node of BST has the following structure:
typedef struct node{
int vale;
struct node* left;
struct node* right;
}node;
The algorithm will have 2 steps:
a. Start from the root node of the Tree and find the starting node and all the ancestor of that node. Store all this in the stack passed:
//root -> root node of the tree.
//val -> value of the node to find.
// s -> stack to store all ancestor.
node* find_node(node* root, int val,std::stack<node*> &s)
{
if(root != NULL) s.push(root);
if(root == NULL || root->value == val) return root;
if(root->value > val) return find_node(root->left);
else return find_node(root->right);
}
and the call to this method would look like:
//Assuming that the root is the root node of the tree.
std::stack<node*> s;
node* ptr = find_node(root,s); // we have all ancestors of ptr along with ptr in stack s now.
b. Now we have to print next k immediate bigger (than ptr) elements of the tree. We will start from the node (which is ptr) :
// s -> stack of all ancestor of the node.
// k -> k-successor to find.
void print_k_bigger(stack<node*> s, int k)
{
//since the top element of stack is the starting node. So we won't print it.
// We will just pop the first node and perform inorder traversal on its right child.
prev = s.top();
s.pop();
inorder(prev->right,k);
// Now all the nodes present in the stack are ancestor of the node.
while(!s.empty() && k>0)
{
//pop the node at the top of stack.
ptr = s.top();
s.pop();
//if the node popped previously (prev) was the right child of the current
//node, i.e. prev was bigger than the current node. So we will have to go
//one more level up to search for successors.
if(prev == ptr->right) continue;
//else the current node is immidiate bigger than prev node. Print it.
printf("%d",ptr->value);
//reduce the count.
k--;
//inorder the right subtree of the current node.
inorder(ptr->right);
//Note this.
prev = ptr;
}
}
Here is how our inorder will look like:
void inorder(node* ptr, int& k)
{
if(ptr != NULL)
{
inorder(ptr->left,k);
printf("%d",ptr->value);
k--;
inorder(ptr->right,k);
}
}
Time Analysis:
The method find_node is O(h) as it can go upto the length max root to leaf path.
The method print_k_bigger is O(h+k) as in every iteration of the loop, either the size of stack is reducing or the value of k is reducing. Note that when inorder() is called from inside the while loop, it won't raise additional overhead as all the calls to inorder() together will take at max O(k).
Here is a very simple algorithm to do this.
Create an empty stack S and a variable curr = NULL.
Find the starting node in the tree. Also, push all of it's ancestors (and the node itself) into stack S
Now pop a node top from the stack S and check if curr is it's right child. If it is not do an inorder traversal of it's right subtree
If we find k or more nodes while traversing, we are done
If we haven't discovered k successors yet, set curr = top, and repeat 2 till we find k nodes
Overall time compleity is O(h+k). Step 1 takes O(h) time. Step 2 takes O(h+k) time (all iterations of step 2 combined take O(h+k) time.)!

Inorder successor of BST without parent info

I was trying to find inorder successor for a node in a BST. And below is my sample code.
public TreeNode getInorderSuccesor(TreeNode t)
{
if(t == null)
{
return null;
}
if(t.getRight()!=null)
{
t = t.getRight();
while(t.getLeft()!=null)
{
t = t.getLeft();
}
return t;
}
else
{
TreeNode parent = t.getParent();
while(parent!=null && parent.getLeft() != t)
{
t = parent;
parent = t.getParent();
}
return parent;
}
}
Please let me know if any condition it will fail. And one more thing if some can share algo/ code/ sudocode for find the inorder successor without parent node. thanks!!!
Here is the basic logic for the successor function; the predecessor function is similar, but reversed. A recursive function descends the tree searching for the requested key, keeping track of the current tree as the possible successor every time if follows the left sub-tree. If it finds the key, its successor is the minimum element of the right sub-tree, found by chasing left sub-trees until reaching a null node. If the search reaches a null node and hence fails to find the key, the next largest key in the tree is the successor, found in the possible successor that was saved at each recursive call. I recently implemented this algorithm at my blog.
If the nodes in the tree have unique values, then it is quite simple. You just need to find:
The node with largest number that is smaller than or equal to the value of the node whose successor needs to be determined
The node with the smallest number that is strictly larger than the value of the node whose successor needs to be determined.
The latter node will be the answer. The first node can be used to check whether the input node is a valid node in the tree.
Pseudo-code:
function inOrderSuccessor(val, tree):
(left, right) = findRange(val, tree.rootnode, null, null)
if (left.val != val):
throw Error("No node with specified value")
return right
function findRange(val, currnode, left, right):
if (currnode == null):
return (left, right)
if (currnode.val <= val):
return findRange(val, currNode.right, currnode, right)
else:
return findRange(val, currNode.right, left, currnode)
If the nodes in the tree may have duplicate value, the in-order successor is not very well-defined. It is necessary to know when you are given a node, how do you differentiate between different nodes with the same value. Are you given a real reference to the node in the tree?
In the tricky case where you are simply given a reference to a node, and there are other nodes with the same value in the tree, then there are 2 cases:
Case 1: The node has right subtree, then you only need to find the smallest element in the right subtree.
Case 2: The node doesn't have right subtree. You need to check reference equality with all the nodes which has the same value. When you have found the node in the tree, traverse upward from the node back to root (you have this in the stack), and find the first node that does a left-turn.

Finding the lowest common ancestor of two nodes in a binary search tree - efficiently

Just wanted to know how efficient is the below algorithm to find the lowest common ancestor of two nodes in a binary search tree.
Node getLowestCommonAncestor (Node root, Node a, Node b) {
Find the in-order traversal of Node root.
Find temp1 = the in-order successor of Node a.
Find temp2 = the in-order successor of Node b.
return min (temp1, temp2);
}
Searching for the lowest common ancestor in a binary search tree is simpler than that: observe that the LCA is the node where the searches for item A and item B diverge for the first time.
Start from the root, and search for A and B at the same time. As long as both searches take you in the same direction, continue the search. Once you arrive at the node such that searching for A and B take you to different subtrees, you know that the current node is the LCA.
A node at the bottom of a large binary search trees can have an in-order successor close to it, for instance if it is the left child of a node, its in-order successor is its parent.
Two nodes descending from different children of the root will have the root as their least common ancestor, no matter where they are, so I believe that your algorithm gets this case wrong.
This is a discussion of efficient LCA algorithms (given time to build a preparatory data structure) at http://en.wikipedia.org/wiki/Lowest_common_ancestor, with pointers to code.
An inefficient but simple way of finding the LCA is as follows: in the tree keep pointers from children to parents and a note of the depth of each node. Given two nodes, move up from the deepest one until the depth if the same. If you are pointing at the other node, it is the LCA. Otherwise move up one step from each node and check again, and so on, until you meet at the LCA.
Finding LCA of BST is straight:
Find the node for which node1 and node2 are present on different
sides. But if the node1 is an ancestor of node2 than also we will
have to return node1. Below code implements this algo.
TreeNode<K> commonAncestor(TreeNode t, K k1, K k2){
if(t==null) return null;
while(true)
{
int c1=t.k.compareTo(k1);
int c2=t.k.compareTo(k2);
if(c1*c2<=0)
{
return t;
}
else
{
if(c1<0)
{
t = t.right;
}
else
{
t = t.left;
}
}
}
}

Construct a binary tree such that the Post-order traversal should give the sorted result

I know that In-order traversal(VISIT LEFT, VISIT ROOT, VISIT RIGHT) on a binary search tree gives me a sorted result. But I need to do a Post-order traversal (VISIT LEFT, VISIT RIGHT, VISIT ROOT) on a binary tree and the result should give me sorted values.
In order to achieve that, how should I construct my binary tree?
Since the root is visited last, it must contain the largest item. Since the left subtree is visited before the right subtree, all items in the left subtree must be smaller than any item in the right subtree.
So to construct a tree like this you can proceed as follows: If you insert an item which is greater than the root, that item becomes the new root. If you insert an item which is smaller than the root but greater than the root of the left subtree, insert it into the right subtree. Else insert it into the left subtree.
You need to ensure the following at each node of the tree:
Value at the node should be greater
than all the values in the
left-subtree and right-subtree.
Values in the left sub-tree should be
less than values in the right
subtree.
This subroutine is for the insertion in the tree
where tree structure is
struct tree
{
int data;
tree * left;
tree *right;
tree(int n) // constructor
{
data = n;
left = right = NULL;
}
};
The algorithm is:
1. If tree is empty insert new node.
2. If data of new node is greater than data of root node make the new node
root of tree.
3. else insert new node in the left subtree of tree.
tree * insert(tree *root,int n)
{
if(root == NULL)
{
root = new tree(n);
return root;
}
else
{
if(n > root -> data)
{
tree * t = new tree(n);
t -> left = root;
return t;
}
else
{
root -> left = insert(root -> left,n);
return root;
}
}
}
The currently accepted answer gives a good on-line algorithm. A somewhat simpler solution---which is not on-line and therefor possibly cheating---is to store a sorted linked list in the parent pointers.
In other words: sort the data; put the largest item in the root, make one of its subtrees empty and recursively construct a post-order-sorted tree of the remaining n-1 items into the other subtree.
The tree will have height n, the single leaf is the head of the list and the root is the tail-most element. If you walk through the tree from leaf to root the elements will form an increasing sequence, and this path will correspond exactly to a postorder traversal.
Deletion
if leaf, then delete regularly
if it has only one son connect son to father
else, delete the root,replace it with its right son,then connect left sub-tree to leftmost vertex in right sub-tree.
for example:
7 6
/ \ / \
3 6 =========DELETING 7 ============> 4 5
/ \ / \ /
1 2 4 5 3
/ \
1 2

Algorithm for successor

I need an algorithm for returning the successor node of some arbitrary node of the
given binary search tree.
To give you an answer with the same level of detail as your question: Go up until going right is possible, and then left until you reach a leaf.
There are two general approaches:
If your binary tree nodes have pointers to their parent node, then you can traverse directly from the node to the successor node. You will have to determine how to use the parent pointers to do the traversal.
If your binary tree nodes do not have parent pointers, then you will have to do an inorder traversal of the tree starting at the root (presumably you have a root node pointer) and return the next node after the given node.
You need to maintain a zipper as you descend the tree. A zipper is simply a list of the nodes you have traversed, and for each an indication of whether you next went left or right.
It allows you to travel back up in the tree even if there aren't any pointers from children to parents.
The algorithm for successor is to go back (up in the zipper) as long as you were coming from the left, then go right once, and then descend to the leftmost child.
It's easier with a figure...
The successor of a node implies you are looking for an inorder successor.
The following method helps you determine the inorder successor WITHOUT ANY PARENT NODE OR EXTRA SPACE NON-RECURSIVELY
struct node * inOrderSuccessor(struct node *root, struct node *n)
{
//*If the node has a right child, return the smallest value of the right sub tree*
if( n->right != NULL )
return minValue(n->right);
//*Return the first ancestor in whose left subtree, node n lies*
struct node *succ=NULL;
while(root)
{
if(n->datadata < root->data)
{
succ=root; root=root->left;
}
else if(n->data > root->data)
root=root->right;
else break;
}
return succ;
}
I'm quite certain this is right. Do correct me if I am wrong. Thanks.

Resources