我需要有一个数据结构用于以下目的.假设我有一个数组a.最初所有元素都设置为零.每当我要更新位置p处具有正值new_value的元素之一时,如果位置p old_value的原始值非零并且大于new_value,那么我需要从位置p开始更新所有非零元素一直到数组的末尾.这里更新意味着使用该位置的旧值和new_value之间的较小值重置值.
例如,数组是:
[2,0,3,0,2,1,5,0,4,0,7]
给定位置2的新值4(从0开始)具有旧值3,我需要将数组更新为:
[2,0,3,0,2,1,4,0,4,0,4]
如果位置2的新值为1,则生成的数组为:
[2,0,1,0,1,1,1,0,1,0,1]
是否有已知的数据结构可以有效地做到这一点?我需要很多这样的更新操作.
谢谢你的建议.
最佳答案 我相信,通过使用splay树的修改,您可以通过每个元素访问或值更改的分摊O(log n)时间来实现此工作.这种方法背后的想法是双重的.首先,我们不是将数组存储为数组,而是将其存储为一对包含原始值的数组,以及一个splay树,其中每个节点的键是数组的索引.例如,给定一个包含七个元素的数组,设置可能如下所示:
Array: 3 1 4 2 5 9 3
Tree: 3
/ \
1. 5
/ \. / \
0. 2 4. 6
请注意,树将索引保存到数组中而不是数组元素本身.如果我们想要在特定索引处查找值,我们只需对索引执行splay树查找,然后在给定位置返回数组元素,该元素占用O(log n)时间.
您希望支持更改所有未来值的操作我将称之为“上限”操作,因为它会在当前值之后设置所有值的上限.考虑这个问题的一种方法是数组中的每个元素都有一个与之相关的上限(最初是无穷大),而元素的真值则是其真实值和上限的最小值.诀窍在于,通过使用展开树,我们可以在摊销的O(log n)时间内调整所有值的上限或超过某一点.为此,我们通过让每个节点存储一个值c来表示从该元素向前强加的上限,然后根据需要对c进行适当的更改来扩充splay树.
例如,假设我们想要从某个元素向前强加一个上限.我们假设这个元素已经在树的根部.在这种情况下,我们只需将其c值设置为O(1)时间内的新上限.从那时起,每当我们查找在该元素之后或之后出现的某个值时,我们就可以记下天花板,因为我们沿着树从根向下行走到相关元素.更一般地说,当我们进行查找时,每次我们遵循正确的子链接时,我们都会注意到该节点的c值.一旦我们遇到了有问题的元素,我们就知道元素的上限,因为我们可以从我们所遵循的正确孩子的根目录中获取路径上节点的最小上限.因此,为了在结构中查找元素,我们进行标准的splay树查找,跟踪c值,然后输出origin数组值和c值的最小值.
但是为了使这项工作,我们的方法必须考虑到展开树进行旋转的事实.换句话说,我们必须展示如何在旋转期间传播c值.假设我们想像这样做一个旋转:
A. B
/. ->. \
B. A
在这种情况下,我们不会更改任何c值,因为在A之后查找的任何值仍将通过A节点.但是,如果我们进行相反的旋转并将A拉到B以上,那么我们将A的c值设置为B的c值和A的c值的最小值,因为如果我们在执行旋转后下降到A的左子树,我们需要因子B的上限考虑在内.这意味着我们每次轮换执行O(1)工作,并且由于每个splay执行的分摊转数是O(log n),因此我们按每次查找分摊O(log n)工作.
为了完成图片,为了更新任意上限,我们将天花板要更改的节点展开到根,然后设置其c值.
简而言之,我们有O(log n)查找O(log n)更改时间(摊销).
这个想法是基于对Sleator和Tarjan的原始论文“Self-Adjusting Binary Search Trees”中链接/剪切树的讨论,该文章还介绍了splay树.
希望这可以帮助!