桶的索引计数

时间:2010-06-25 17:22:18

标签: algorithm data-structures b-tree

所以,这是我的小问题。

假设我有一个桶子列表a 0 ... a n ,它们分别包含L< = c 0 .. .c n < H项目。我可以决定L和H的限制。我甚至可以动态更新它们,但我认为它不会有多大帮助。

桶的顺序很重要。我不能去交换它们。

现在,我想将这些存储桶编入索引,以便:

  • 我知道项目总数
  • 我可以查找第i个元素
  • 我可以添加/删除任何存储桶中的项目并有效地更新索引

好像很容易吗?看到这些标准,我立即想到了Fenwick树。这就是他们真正想要的。

但是,当您考虑用例时,其他一些用例会出现:

  • 如果铲斗数量低于L,铲斗必须消失(不要担心物品)
  • 如果存储桶计数达到H,则必须创建新存储桶,因为此存储桶已满

我还没有想出如何有效地编辑Fenwick树:删除/添加节点而不重建整个树...

当然我们可以设置L = 0,因此删除会变得不必要,但是无法真正避免添加项目。

所以这就是问题:

您知道这个索引的更好结构还是如何更新Fenwick树?

主要关注的是效率,因为我计划实施缓存/内存考虑值得担心。

背景

我试图提出一个类似于B-Trees和Ranked Skip Lists但具有本地化索引的结构。这两种结构的问题在于索引是沿着数据保存的,这在缓存方面是低效的(即你需要从内存中获取多个页面)。数据库实现表明,保持索引与实际数据隔离对缓存更友好,因此更有效。

2 个答案:

答案 0 :(得分:3)

我已将您的问题理解为:

每个桶都有一个内部订单,桶本身有一个订单,所以所有元素都有一些排序,你需要在那个订单中使用第i个元素。

要解决这个问题:

您可以做的是维护一个“累积值”树,其中叶节点(x1,x2,...,xn)是桶大小。节点的值是其直接子节点的值的总和。保持2的幂将使它变得简单(你总是可以用零大小的桶来填充它)并且树将是一个完整的树。

对应每个存储桶,您将维护指向相应叶节点的指针。

例如,说桶大小是2,1,4,8。

树看起来像

     15
    /  \
   3    12
  / \  / \
 2  1  4  8

如果您想要总计数,请阅读根节点的值。

如果你想修改一些xk(即改变相应的桶大小),你可以按照父指针向上走树,更新值。

例如,如果您向第二个存储桶添加4个项目(标有*的节点是更改的节点)

     19*
    /   \
   7*    12
  / \   / \
 2  5*  4  8

如果要查找第i个元素,可以沿着上面的树行走,有效地进行二分查找。您已经有一个左孩子和右孩子。如果我>左子节点当前节点的值,您减去左子节点值并在右侧树中递归。如果i< =左子节点值,则向左移动并再次递归。

假设你想在上面的树中找到第9个元素:

由于root的左子女是7 < 9。 你从9减去7(得到2)然后向右走。

从2&lt; 4(左边的12个孩子),你往左走。

您位于与第三个存储桶对应的叶节点。您现在需要选择该存储桶中的第二个元素。

如果你必须添加一个新的存储桶,你可以通过添加一个新根来增加树的大小(如果需要),使现有树成为左子节点,并添加一个除了添加的存储树之外的所有零存储桶的新树(我们是新树的最左边的叶子)。这将在O(1)时间内分摊,以便为树添加新值。警告你最后只能添加一个桶,而不是中间的任何地方。

获得总数是O(1)。 更新单个存储桶/项目查找是O(logn)。

添加新桶分摊O(1)。

空间使用是O(n)。

您可以使用B树进行相同的操作,而不是二叉树。

答案 1 :(得分:0)

我仍然希望得到答案,但是在@Moron建议之后,这是我到目前为止所能提出的。

显然,我的小 Fenwick Tree 想法无法轻易改编。在fenwick树的末端添加新桶很容易,但在中间没有它,所以它是一种失败的原因。

我们留下了2个数据结构:二进制索引树(具有讽刺意味的是Fenwick用来描述他的结构的名称)和排名跳过列表。

通常,这不会将数据与索引分开,但我们可以通过以下方式获得此行为:

  1. 使用间接:节点持有的元素是指向存储桶的指针,而不是存储桶本身
  2. 使用池分配,以便索引元素,即使彼此独立分配,仍然在内存中关闭,这将有助于缓存
  3. 我倾向于选择Skip Lists到Binary Trees,因为它们是自组织的,所以我不遗余力地不断地重新平衡我的树。

    这些结构允许到O(log N)中的第i个元素,我不知道是否有可能获得更快的渐近性能。

    另一个有趣的实现细节是我有一个指向这个元素的指针,但其他人可能已被插入/删除,我现在如何知道我的元素的等级?

    如果存储桶指向拥有它的节点,则可能。但这意味着要么节点不应该移动,要么在移动时应该更新存储桶的指针。