我正在寻找一个合适的结构来处理以下问题:
可以按任何顺序接收这些页面。一组页面P1..P10的示例:
我想找到一个允许我执行以下操作的结构:
基本上,我想要实现的是在接收推文时有效地存储在推文的内存页面中(Twitter timeline)。我当然可以使用数组或链接列表但这意味着O(n)插入和查询时间......当然,我需要能够根据列表中的“位置”查询项目以显示它们在ListView中。
我想了几个解决方案,但没有一个是合适的:
我希望我的问题得到明确说明。关于可能有效扩展的有效解决方案的想法吗?
编辑:我想查询不在内部项目ID上的页面(例如T140,T150或其他任何内容),而是查询Element(即Tweet)索引。例如,在我的第一个例子中,T120将是第21个项目(因为有一个包含20个元素的页面P1)。所以我希望能够查询一个间隔[20-29],它应该返回元素[T120,...]。我不想直接搜索120.
答案 0 :(得分:2)
您可以使用线程平衡二叉搜索树。但是,在搜索中,您不会针对节点x
中的单个号码检查号码n
,而是针对页面px
检查页面pn
。由于您的页面不重叠,因此非常简单。在您选择的px
(x
)中输入一个ID,然后根据pn
(pn_min
,pn_max
的最小值和最大值进行检查。然后:
if pn_min <= x <= pn_max
the page you are looking for
if x < pn_min
go left
if x > pn_max
go right
为了能够检索某个范围内的ID,您首先要在树中找到该范围的最小值(x
)(使用普通搜索)。如果它不存在,则意味着您已经搜索到了一片叶子。称之为pn
:
if x < pn_min
start from pn
if x > pn_max
start from pn->next
其中pn->next
是线程树中的下一个节点。现在你有了一个起始页面。只需浏览页面并检索ID,直到达到范围的最大值。如果页面结束,请转到线程树中的next
页面并继续如上所述。
由于树是平衡的,这应该在搜索/插入/删除操作中为您提供O(logn)
,并且由于它是线程化的,它应该为您提供O(logn + k)
(其中k
是区间查询中的给定范围内的ID。
注意:您的树不需要在两个方向都有线程。 GNU's libavl似乎有你需要的工具,但如果它更简单,或者你必须自己编写,你可以只考虑一个右线程树。
修改:根据r
到s
ID查询范围。
稍作修改,你也可以做到这一点。该算法与查找实际ID的范围相同,只是查找第一个元素是不同的。
让我们在每个节点上附加一个数字,说明在该节点左侧插入了多少个ID。请拨打此pn_before
。另外,请将pn_size
称为pn
中的ID数。现在搜索rth
id(这是[rth, sth]
ID范围内的第一个)如下:
passed = 0
pn = root
while pn not leaf
if passed + pn_before < rth <= passed + pn_before + pn_size
the node you are looking for
if rth <= passed + pn_before
go left
if rth > passed + pn_before + pn_size
passed += pn_before + pn_size
go right
要解释什么是passed
,请想象以下树
__________ p3 {5, 6} before: 4___________
/ \
______p2 {2, 3, 4} before: 1 _______p5 {9}: before 2_____
/ / \
p1 {1} before: 0 p4 {7, 8}: before 0 p6 ...
现在假设你正在寻找第7个元素(在这个例子中也有id 7)。如果你查看根(p3
),你会看到它前面有4个ID,其中有2个ID。因此,第3个如果适用,你就是正确的。现在在这个新树中,你知道你已经传递了4 + 2个ID,所以你不必寻找第7个元素,而是寻找第1个元素。变量passed
有助于跟踪右转时跳过的id。
或者,您可以从pn_before
缩小pn_size
和rth
,因此rth
每次都会变小。它是一样的(但记得备份rth
,因为你以后需要它)
找到rth
元素的位置后,您将继续作为上一个间隔查询算法。
现在只剩下问题是更新pn_before
。这很简单。由于每个子树的每个根只跟踪它的左子树,然后在插入/删除时,你需要向上到树的根并添加/删除该节点的pn_before
数量刚插入/删除的ID。记得只改变从左边孩子上去的父母。如果您去找父母并且您是正确的孩子,则父母不需要跟踪您。请注意,在这种情况下,您不应该停止,因为父级可能是其父级的左子级。
在纸上做,你会得到它;)
另一个注意事项:重新平衡树时要注意pn_before
。
再次搜索O(logn + k)
,其中k
是您要查询的时间间隔内的ID数(s - r
)。插入/删除后向后退的附加步骤不会更改这些算法的顺序,因为后退步骤也是O(logn)
。