问题
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.
回答1:
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).
回答2:
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).
回答3:
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 stackS
and check ifcurr
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.)!
来源:https://stackoverflow.com/questions/40561235/time-complexity-of-finding-k-successors-in-bst