我试图理解我应该如何考虑在b树中获取第k个键/元素。即使是步骤而不是代码,它仍然会有很大帮助。谢谢
编辑:为了澄清,我要的是B-树中第k个最小的键。
最佳答案
使用标准的b树是没有效率的。大体而言,我认为有两种选择:
将b树转换为order statistic tree以允许在o(log n)中执行此操作。
也就是说,对于每个节点,保留一个变量,该变量表示根在该节点(该节点、其所有子节点、其所有子节点等)的子树的大小(元素数)。
无论何时插入或删除,都会相应地更新此变量您只需要更新已经访问过的节点,这样就不会改变这些操作的复杂性。
要得到k
-th元素,需要将孩子的大小相加,直到我们到达k
,选择合适的孩子进行访问,并适当减少k
.伪代码:
select(root, k) // initial call for root
// returns the k'th element of the elements in node
function select(node, k)
for i = 0 to t.elementCount
size = 0
if node.child[i] != null
size = node.sizeOfChild[i]
if k < size // element is in the child subtree
return select(node.child[i], k)
else if k == size // element is here
&& i != t.elementCount // only equal when k == elements in tree, i.e. k is not valid
return t.element[i]
else // k > size, element is to the right
k -= size + 1 // child[i] subtree + t.element[i]
return null // k > elements in tree
认为
child[i]
直接位于element[i]
的左侧。Wikipedia中提供的二进制搜索树(不是b-树)的伪代码可以更好地解释这里的基本概念。
注意,节点子树的大小应该存储在其父节点中(注意,我没有使用上面的
node.child[i].size
)。将它存储在节点本身的效率要低得多,因为对于B树用例(节点通常必须从磁盘读取)来说,读取节点被认为是一个不平凡或代价高昂的操作,因此您希望将读取的节点数量最小化,即使这会使每个节点稍微大一点。做一个in-order traversal直到你看到
k
元素-这需要O(n)。伪代码:
select(root, *k) // initial call for root
// returns the k'th element of the elements in node
function select(node, *k) // pass k by pointer, allowing global update
if node == null
return null
for i = 0 to t.elementCount
element = select(node.child[i], k) // check if it's in the child's subtree
if element != null // element was found
return element
if i != t.elementCount // exclude last iteration
if k == 0 // element is here
return t.element[i]
(*k)-- // only decrease k for t.element[i] (i.e. by 1),
// k is decreased for node.child[i] in the recursive call
return null
关于c - 在B树中找到第k个 key 的算法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22414339/