问题:
我的代码如下(不起作用):
public K from(K k, int i) throws NotFound {
//reset stack
parentNode = new Stack<Node<K>>();
//Returns node with value k
Node<K> foundNode = findNode(k, root);
return fromHelper(k, i, foundNode);
}
public K fromHelper(K k, int i, Node<K> v) throws NotFound {
if ( v == null ) {
throw new NotFound();
}
else if (i == 0) {
return v.key;
}
//Case 3: left tree exists
if (v.left != null) {
if (k.compareTo(v.left.key) < 0) {
return fromHelper(k, i-1, v.left);
}
}
//Case 2: Right tree exists
if (v.right != null) {
return fromHelper(k, i-1, v.right);
}
if (!parentNode.isEmpty()) {
//Pop parent node
Node<K> parent = parentNode.pop();
if (k.compareTo(parent.key) < 0) {
return fromHelper(k, i-1, parent);
}
else {
throw new NotFound();
}
}
else {
throw new NotFound();
}
}
我完全迷失了这个问题。我绝对不知道如何获得在O(log(n))时间内运行的算法。我能想到的唯一算法是进行有序遍历,在数组中存储值然后返回大于k的第i个最小密钥(如果存在),但显然这是最坏情况O(n)。过去两天我一直在研究这个问题,我根本无法找到解决方案。我的教授给我的提示是向节点添加一个大小字段,它将告诉我子树v中包含自身的节点数。我的想法是尝试使用relative_rank函数(给我节点值相对于节点v的等级),但我想不出在O(log(n))时间内这样做的方法。任何帮助将不胜感激。
答案 0 :(得分:0)
在思考了一下之后,我会给你一些代码片段,你可以用它来自己改进这个方法。
前提条件:您必须将size字段添加到每个节点。每次插入和删除时都必须更新此大小字段。当然,当旋转一些子树时。
有了这样的尺码信息,您应该可以这样做:
public K from(K k, int i) {
Node<K> foundNode = findNode(k, root);
if (foundNode == null)
throw new NotFound();
return (i == 0)? foundNode.key : fromHelper(foundNode.right, i - 1);
}
这反映了如果i == 0
直接停止的想法。如果没有,那么请查看正确的子树,因为只有这个包含更大的值。
private K fromHelper(Node<K> node, int i) {
if (node == null || node.size() <= i)
throw new NotFound();
if (i == 0)
return node.key;
int sizeLeft = (node.left == null)? 0 : node.left.size();
return (sizeLeft < i)? fromHelper(node.right, i - sizeLeft) : fromHelper(node.left, i - 1);
}
关键技巧是确定左子树的剩余节点数(带有大小信息),然后根据剩余的数量走向左侧或右侧子树。