给出一个大小为n的数组(n为2的幂)。数组的所有条目都初始化为零。您必须执行以下在线操作的序列:
Add(i,x)
将x
添加到条目A[i]
。sum(i,j)
=数组中条目的总和,从i
到j
,适用于任何0 < i < j <= n
。可以很容易地看出,我们可以在O(1)
时间内执行第一次操作,而在最坏的情况下,第二次操作可能会花费O(n)
。您的目标是有效地执行这些操作。提供一个数据结构,保证每次操作O(log n)
次
我认为解决方案是细分树,但确实知道我错了还是错了?
答案 0 :(得分:1)
嗯,您可以使用树来执行此操作,或者您可以使用更高效的数据结构来执行此操作。在任何一种情况下,您都不需要分段树 - 这些分支树更加面向搜索,而且更少数据导向。
出于效率的原因,我会选择一组数组,其中第一个数组是n大小,下一个数组是n / 2大小,n / 4,n / 8,... 1.每当你添加x到A [i](第一个数组),你还将它添加到第二个数组中的索引(i&gt;&gt; 1)(又名i / 2),索引(i&gt;&gt; 2)(又名i / 4)在第三个阵列等...
诀窍在于计算sum(i,j)。我们现在要计算sum(0,j)和sum(0,i - 1),因为sum(i,j)= sum(0,j) - sum(0,i - 1)。
要计算sum(0,v)(对于某些v),您所要做的就是从每个级别最多添加一个条目。从0到索引* 的总和不包括索引本身的伪代码如下:
sum from 0 to index:
begin
i = 0
sum = 0
while i < maxlevel
if ((index >> i) & 1) != 0
sum = sum + array[i][index]
return sum
end
为了直观了解其工作原理,请考虑最低级数组,并将0从偶数指数与奇数指数相加。如果你使用偶数索引,你可以通过使用它上面的数组来更高效地完成它,因为它已经有两个条目总和了!实际上,对于奇数索引,您可以像对偶数索引那样做同样的事情,但是手动添加一个“奇怪的人”的条目。但是既然你要在第二级汇总指数,你可以做所有但可能是最后一个(如果总和的新指数)是奇数......等等等。这正是我们'在这里做。
那些应该是你需要的所有部分。如果你想用二叉树来做,你仍然可以采用相同的方式。您可以在二叉树的每个节点中保留一个总和。在遍历以向特定节点添加总和时,还要将其添加到您遍历的所有节点。同样,您可以使用相同的技巧来找到零索引技巧。找到从零到索引的总和现在变为二叉树遍历,对于您遍历的每个节点,添加节点的总和并减去不遍历的任何“右手”(或更高值)的子项。 / p>
希望我没有破坏作业问题,通过阅读本文,你有足够的味道来完成它,但不是太多,以至于问题现在被简单化为仅仅是实施。
祝你好运!
答案 1 :(得分:0)
树解决方案:
创建一个完整的树,其叶子是你的主菜:1,2,...,n
node.value = inserted value [if node is a leaf]
node.value = node.left.value + node.right.value [if node is not a leaf]
更新主菜是O(logn),因为您需要一直更新总和到根。
对于sum(i,j),我们将找到所有元素的总和留给i,所有元素都直接到j,并从root.value中减去它,我们将得到我们需要的东西。 这样:
sum(root,i,j) = root.value - sumLeft(i,root) - sumRight(j,root)
sumLeft()
和sumRight()
的计算很简单,[对于sumLeft:]只是从根到下限开始,每次向左,减去右子值,通过这样做,你可以减少不需要的节点。 [说服自己为什么这是真的]。最后,总和只有相关的节点。 [sumRight的原理相同,但我们减去左边的儿子]。
伪代码:
sumLeft(x,root):
curr <- root
sum <- root.value
while (curr is not a leaf):
if (x is in curr.right):
curr <- curr.right
else:
sum <- sum - curr.right.value
curr <- curr.left
return sum
sumRight(x,root):
curr <- root
sum <- root.value
while (curr is not a leaf):
if (x is in curr.left):
curr <- curr.left
else:
sum <- sum - curr.left.value
curr <- curr.right
return sum
所以,sum(root,i,j)需要你两次下去树,根据需要仍然是O(logn)。
EDIT1:
这是你如何在root上添加x到entree i:[问题表明oporation正在添加到主菜而不是替换它,所以这就是这个解决方案所假设的。它也可以很容易地修改为插入]。
add(root,i,x):
root.value <- root.value + x
if root is leaf:
return
else if i is in root.left:
add(root.left,i,x)
else:
add(root.right,i,x)
请注意,您可以确切地知道根的哪个子树(左侧或右侧),主菜我属于[如果i < n/2
在左侧,或者在右侧]。解释一下自己是如何为任何子树做的,而不仅仅是根。
编辑2: 具有8个元素的树的示例:
21
10 11
4 6 7 4
1 3 2 4 6 1 2 2
总结示例:
总和(4,6)应提供4 + 6 + 1 = 11
。
1 + 3 + 2 = 6
,并按预期提供:sumLeft(4) = 21 - 11 - 4 = 6
。
2 + 2 = 4
,并提供sumLeft(6) = 21 - 11 - 7 =4
。 [按预期]
sum(4,6) = 21 - 6 - 4 = 11
如预期的那样。
添加示例:
添加(2,4)[向第二个元素添加4]将导致将树修改为:
25
14 11
8 6 7 4
1 7 2 4 6 1 2 2