我见过许多使用O(4 * N)内存的段树实现。无论如何,段树是否正好使用2 * N-1内存?我找不到任何执行此操作的实现。即使他们谈论空间复杂度实际上是2 * N-1,他们仍然会分配4 * N.
编辑:我的意思是对于n个数字的数组,您使用一个2n-1数字的数组作为分段树。每个实现使用2 *的数组(下一个2的幂大于n)。我知道O符号省略了常量,但对于非常大的n,如果它是2 * N-1或4 * N则很重要,因此我问这个问题。
答案 0 :(得分:2)
O(4 * N)= O(2 * N)= O(N)因为O符号确实抽象了常数因子。
所以你的意思是"准确使用2 * N-1内存"?你的意思是2 * N-1字节?如果你的意思是O(2 * N-1),那么它与O(4 * N),O(2 * N)和O(N)相同。
答案 1 :(得分:1)
根据Big O notation,O(4 * N)= O(2 * N-1)= O(N)。段树的任何标准实现都使用线性存储器。
回到你的问题,考虑一个数组a[0...n-1]
作为输入。考虑n = 10
。构建分段树时,它看起来如下所示:
用于构造的节点数= 2 * n - 1 = 19.因此,理论上,只需要19个节点!
但是,我们正在使用array
数据结构实现分段树。这里,每个节点将对应array
中的某个索引。在细分树中,对于已编入索引的节点x
,左子索引= 2*x
,右子索引= 2*x+1
。
所以,如果我从一个索引数组开始,索引为[0, 9] = 1, [5] = 24, [6] = 25
。所以,在这种情况下,至少需要索引到25
。在最坏的情况下,如果完全成长的细分树31
,则需要(n=16)
的索引。
因此,我们维持array
的大小为2*(next power of 2 >= n)
答案 2 :(得分:0)
大多数人都熟悉实现安全的 4N 分配内存,这恰好是在大小为 N 或OOP版本的数组上构建段树的上限。有关其背后的一些想法,请阅读here。这是一个很好的资源,并且在分段树上有很多信息。
现在,进入 2N 内存使用情况。看看here,here,还有:
有效实现内存(来自here)
大多数人都使用上一部分中的实现。如果看一下数组t,您会发现它按照BFS遍历的顺序(级别顺序遍历)跟随树节点的编号。使用该遍历,顶点v的子代分别为2v和2v + 1。但是,如果n不是2的幂,则此方法将跳过某些索引,并保留数组t的某些部分未使用。即使n个元素的数组的分段树仅需要2n-1个顶点,内存消耗也受到4n的限制。 但是可以减少。我们按照欧拉遍历(预遍历)的顺序对树的顶点重新编号,然后将所有这些顶点彼此相邻地写。
让我们看一下索引v处的一个顶点,让他负责段[l,r],让mid = l + r2。显然,左孩子将具有索引v + 1。左孩子负责段[l,mid],即在左孩子的子树中总共有2 *(mid-1 + 1)-1个顶点。因此,我们可以计算v的右子项的索引。索引将为v + 2 *(mid-1 + 1)。通过此编号,我们将所需的内存减少到2n。
答案 3 :(得分:0)
您可以将节点动态添加到树中。这样,您不使用的节点将不存在,也不会浪费内存。