n
列表大小v[n]
列出所有值设置为0
update(k, a)
的v[k] += a
操作
query(a, b)
操作,返回v[a] + v[a+1] + ... + v[a+b]
,a<b
这些操作使算法的时间复杂度为O(n * cost of one operation)
:
---------------------------
| update | query | total |
---------------------------
| O(1) | O(n) | O(n) |
---------------------------
是否有任何版本的update
和query
操作可以提高总时间复杂度?
例如,我尝试在0
操作中缓存n
和update
之间的每个总和,但我得到了更慢的算法:
---------------------------
| update | query | total |
---------------------------
| O(n^2) | O(1) | O(n^2) |
---------------------------
有什么建议吗?
最糟糕的情况是我感兴趣的。
我已经知道两个版本:每个版本都有一个操作O(1)
而另一个O(n)
或更高版本。
答案 0 :(得分:2)
正如Niklas所说,这个问题或多或少是为Fenwick trees设计的(你的查询可以作为两个前缀和的差异来回答)。我正在写这个答案,指出可以用不同的方式来换取运营成本。
首先,使用廉价查询的算法可以将其更新时间改进为O(n):提前计算前缀总和并使用上述差异技巧。此外,我们可以提取主要想法并将其应用于任何支持操作的数据结构
update(k, a, b): for i in a..b, do s[i] += k
query(i): return s[i],
其中s
现在包含前缀sum而不是实际值。
现在,经典的Fenwick / segment树每个内部节点有d = 2个子节点(是二进制的)。没有什么能阻止我们选择其他d值。 update
或query
必须访问节点及其祖先,而另一个必须访问包含输入间隔的段。前一种访问模式需要时间O(log n / log d)。后一种模式需要时间O(d(log n / log d))。在此框架中,您提出的算法实质上取d = n。通过获取d的其他值,我们可以考虑查询/更新操作的精确组合以及可能有利于更平坦的树结构的架构细节。
答案 1 :(得分:1)
感谢@Niklas B的评论之一。我继续关注细分树的研究。
细分树的表示
i
的每个节点,左边的子节点位于2*i+1
,右边的子节点位于2*i+2
,父节点位于floor((i-1)/2)
从给定数组构建段树
arr[0, .., n-1]
n
叶子的完整二叉树,因此会有n-1
个内部节点2*n – 1
。时间复杂度
O(n)
2n-1
个节点,每个节点的值仅在树构造中计算一次O(Logn)
O(Logn)
要更新叶值,我们处理每个级别的一个节点,级别数为O(Logn)
-------------------------------
| update | query | total |
-------------------------------
| O(Logn) | O(Logn) | O(Logn) |
-------------------------------
参考文献:
http://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/