The example described above can very well be described in recursive form. The code below is a complete program in recursive form.
//find kth element in a BST recursively
Node * find_kth_element(Node *tree, int &k) {
if(tree == 0)
return 0;
int count = 0;
//count the nodes in left subtree
count_nodes(tree->left_, count);
//check if the root is median
if( k == count + 1)
return tree;
//check if median falls on left subtree
else if (count >= k) {
return find_kth_element(tree->left_,k);
}
//the median falls on right subtree
else {
k = k - (count + 1);
return find_kth_element(tree->right_, k);
}
}
Iterative Solution
We know that recursive solutions are not that efficient and it's always good to avoid it if it's possible. The problem however is equally simple in iterative form as well. The code below translates recursive form into iterative form.
//find kth element iteratively
Node * find_kth_element_iterative(Node *tree, int &k) {
if(tree == 0)
return 0;
Node *node = tree;
int count = 0;
int pos = k;
while(node != 0) {
count = 0;
//count nodes on the left subtree
count_nodes(node->left_, count);
//check if root is the median
if( pos == count + 1)
return node;
//check if median falls on left subtree
else if (count >= pos)
node = node->left_;
//median falls on right subtree
else {
pos -= (count + 1);
node = node->right_;
}
}
return 0;
}
Complexity
What's the complexity of above algorithms? While finding median You are moving along a path in the tree, so in the worst case it can be height of the tree (h) and also you are counting the number of nodes in left subtree at each step. Which requires O(n) time in the worst case. Thus the overall algorithm is O(nh).
Is there a way to improve it? Yes, of course ( at least one technique I know of). If you need to keep on computing the kth median on a single tree many times, then you can construct a tree with a count of elements in its subtree. If you do this the counting of number of nodes in left subtree takes O(1) time. Effectively the algorithm will reduce to O(h) algorithm. In case tree is balanced this is just O(log n). Not bad :).
I have thought of a way to find median in O(1). Tweak your insert function and delete function to always maintain the median.
ReplyDeleteSimilarly for finding kth median where k is given, maintain that kth median while inserting or deleting. Thus it is also O(1). But here is the catch, it will work only where k is given up front.
Cant you go to extreme left and use in-order traversal solve this ?, it will save counting nodes or left every time ., just a thought ...
ReplyDeleteBut that will take O(n) space which is not very efficient when the tree is way too large. Comments?
DeleteI have written the following function that finds the kth element in tree.
ReplyDeleteNote that pos and res are passed bny reference. pos is used to store current index and res is used to store the kth element if found.
Your comments are highly appreciable.
void findElement(Node n,ref int pos,ref Node res){
if(n==null||res!=null)return;
findElement(n.left,pos,res);
if(res!=null)return;
if(pos==k)res=n;
else{
pos++;
findElement(n.right,pos,res);
}
}