Inorder successor of BST without parent info - algorithm

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.

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.)!

Runtime of the following recursive algorithm?

I am working through the book "Cracking the coding interview" by Gayle McDowell and came across an interesting recursive algorithm that sums the values of all the nodes in a balanced binary search tree.
int sum(Node node) {
if (node == null) {
return 0;
}
return sum(node.left) + node.value + sum(node.right);
}
Now Gayle says the runtime is O(N) which I find confusing as I don't see how this algorithm will ever terminate. For a given node, when node.left is passed to sum in the first call, and then node.right is consequently passed to sum in the second call, isn't the algorithm computing sum(node) for the second time around? Wouldn't this process go on forever? I'm still new to recursive algorithms so it might just not be very intuitive yet.
Cheers!
The process won't go on forever. The data structure in question is a Balanced Binary Search Tree and not a Graph which can contain cycles.
Starting from root, all the nodes will be explored in the manner - left -> itself -> right, like a Depth First Search.
node.left will explore the left subtree of a node and node.right will explore the right subtree of the same node. Both subtrees have nothing intersecting. Draw the trail of program control to see the order in which the nodes are explored and also to see that there is no overlapping in the traversal.
Since each node will be visited only once and the recursion will start unwinding when a leaf node will be hit, the running time will be O(N), N being the number of nodes.
The key to understanding a recursive algorithm is to trust that it does what it is deemed to. Let me explain.
First admit that the function sum(node) returns the sum of the values of all nodes of the subtree rooted at node.
Then the code
if (node == null) {
return 0;
}
return sum(node.left) + node.value + sum(node.right);
can do two things:
if node is null, return 0; this is a non-recursive case and the returned value is trivially correct;
otherwise, the fuction computes the sum for the left subtree plus the value at node plus the sum for the right subtree, i.e. the sum for the subtree rooted at node.
So in a way, if the function is correct, then it is correct :) Actually the argument isn't circular thanks to the non-recursive case, which is also correct.
We can use the same way of reasoning to prove the running time of the algorithm.
Assume that the time required to process the tree rooted at node is proportional to the size of this subtree, let |T|. This is another act of faith.
Then if node is null, the time is constant, let 1 unit. And if node isn't null, the time is |L| + 1 + |R| units, which is precisely |T|. So if the time to sum a subtree is proportional to the size of the subtree, the time to sum a tree is prortional to the size of the tree!

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;
}
}
}
}

Need help understanding inorder successor in binary search tree

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!

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