我需要在二叉搜索树中找到第k个最小的元素,且不能使用任何静态/全局变量。如何高效实现?
我目前考虑的解决方案是,最坏情况下通过遍历整棵树进行O(n)操作。但我感觉这里没有充分利用二叉搜索树的性质。请问我的解决方案是否正确,还有更好的方案吗?
我需要在二叉搜索树中找到第k个最小的元素,且不能使用任何静态/全局变量。如何高效实现?
我目前考虑的解决方案是,最坏情况下通过遍历整棵树进行O(n)操作。但我感觉这里没有充分利用二叉搜索树的性质。请问我的解决方案是否正确,还有更好的方案吗?
更简单的解决方案是进行中序遍历并跟踪当前要打印的元素(但不要打印它)。当我们到达第k个元素时,打印该元素并跳过树的其余遍历。
void findK(Node* p, int* k) {
if(!p || k < 0) return;
findK(p->left, k);
--k;
if(k == 0) {
print p->data;
return;
}
findK(p->right, k);
}
public int ReturnKthSmallestElement1(int k)
{
Node node = Root;
int count = k;
int sizeOfLeftSubtree = 0;
while(node != null)
{
sizeOfLeftSubtree = node.SizeOfLeftSubtree();
if (sizeOfLeftSubtree + 1 == count)
return node.Value;
else if (sizeOfLeftSubtree < count)
{
node = node.Right;
count -= sizeOfLeftSubtree+1;
}
else
{
node = node.Left;
}
}
return -1;
}
这是我基于上述算法在C#中实现的代码,我想发帖分享一下,让大家更好地理解。它对我很有效。
谢谢IVlad。
public static int kthSmallest (Node pivot, int k){
if(pivot == null )
return k;
k = kthSmallest(pivot.left, k);
k--;
if(k == 0){
System.out.println(pivot.value);
}
k = kthSmallest(pivot.right, k);
return k;
}
//添加一个无需使用递归的Java版本
public static <T> void find(TreeNode<T> node, int num){
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
TreeNode<T> current = node;
int tmp = num;
while(stack.size() > 0 || current!=null){
if(current!= null){
stack.add(current);
current = current.getLeft();
}else{
current = stack.pop();
tmp--;
if(tmp == 0){
System.out.println(current.getValue());
return;
}
current = current.getRight();
}
}
}
Time Complexity: O( N ), N is the number of nodes
Space Complexity: O( 1 ), excluding the function call stack
// Private Helper Macro
#define testAndReturn( k, counter, result ) \
do { if( (counter == k) && (result == -1) ) { \
result = pn->key_; \
return; \
} } while( 0 )
// Private Helper Function
static void findKthSmallest(
BstNode const * pn, int const k, int & counter, int & result ) {
if( ! pn ) return;
findKthSmallest( pn->left_, k, counter, result );
testAndReturn( k, counter, result );
counter += 1;
testAndReturn( k, counter, result );
findKthSmallest( pn->right_, k, counter, result );
testAndReturn( k, counter, result );
}
// Public API function
void findKthSmallest( Bst const * pt, int const k ) {
int counter = 0;
int result = -1; // -1 := not found
findKthSmallest( pt->root_, k, counter, result );
printf("%d-th element: element = %d\n", k, result );
}
注(与@prasadvk的解决方案不同之处):
需要在三个位置进行if( counter == k )
测试:(a)在左子树后,(b)在根节点后,以及(c)在右子树后。这是为了确保检测到所有位置的第k个元素,即无论它位于哪个子树中。
需要if( result == -1 )
测试以确保仅打印结果元素,否则将打印从第k小的元素开始到根节点的所有元素。
O(k + d)
,其中d
是树的最大深度。因此它使用了一个全局变量counter
,但对于这个问题来说是不合法的。 - Valentin Shergin如果只有一个普通的二叉搜索树,你只能从最小值开始向上遍历,找到正确的节点。
如果你经常这样做,可以为每个节点添加一个属性,表示其左子树中有多少个节点。利用这一点,你可以直接下降到正确的节点。
签名:
Node * find(Node* tree, int *n, int k);
调用方式:
*n = 0;
kthNode = find(root, n, k);
定义:
Node * find ( Node * tree, int *n, int k)
{
Node *temp = NULL;
if (tree->left && *n<k)
temp = find(tree->left, n, k);
*n++;
if(*n==k)
temp = root;
if (tree->right && *n<k)
temp = find(tree->right, n, k);
return temp;
}