Optimization 优化整数数组的和(i,j)和更新(i,值)
给定一个庞大的整数数组,优化函数sum(i,j)和update(i,value),使这两个函数都小于O(n) 更新 这是一个面试问题。我试过O(n)和(I,j)和O(1)更新(I,值)。另一种解决方案是将输入数组预处理为二维数组,给出求和(i,j)的O(1)解。但这使得O(n)的更新函数 例如,给定一个数组:Optimization 优化整数数组的和(i,j)和更新(i,值),optimization,segment-tree,Optimization,Segment Tree,给定一个庞大的整数数组,优化函数sum(i,j)和update(i,value),使这两个函数都小于O(n) 更新 这是一个面试问题。我试过O(n)和(I,j)和O(1)更新(I,值)。另一种解决方案是将输入数组预处理为二维数组,给出求和(i,j)的O(1)解。但这使得O(n)的更新函数 例如,给定一个数组: A[] = {4,5,4,3,6,8,9,1,23,34,45,56,67,8,9,898,64,34,67,67,56,...} 要定义的操作是sum(i,j)和update(i,va
A[] = {4,5,4,3,6,8,9,1,23,34,45,56,67,8,9,898,64,34,67,67,56,...}
要定义的操作是sum(i,j)
和update(i,value)
给出从索引sum(i,j)
到i
的数字总和j
使用给定的update(i,value)
值更新索引
处的值i
sum(i,j)
可以在O(n)
时间中计算,并在O(1)
时间中更新(i,值)
第二种方法是预先计算sum(i,j)
并将其存储在二维数组sum[n][n]
中,当查询时,在O(1)
时间内给出答案。但是更新函数update(i,value)
变得有序O(n)
,因为必须更新与索引i
相对应的整行/列
面试官暗示我要做一些预处理和使用一些数据结构,但我想不出来。你需要的是段树。段树可以在
O(log(n))
时间中执行sum(i,j)
和update(i,value)
引自:
在计算机科学中,段树是用于存储区间或段的树数据结构。它允许查询哪些存储段包含给定点。原则上,它是一个静态结构;也就是说,一旦建成,其结构就无法修改
树的叶子将是初始数组元素。他们的父母将是他们孩子的总和。例如:假设data[]={2,5,4,7,8,9,5}
,那么我们的树将如下所示:
此树结构使用数组表示。让我们调用这个数组seg_tree
。因此seg_树[]的根将存储在数组的索引1中。它的两个子项将存储在索引2和3中。1-indexed
表示法的总体趋势是:
- 索引
的左子级位于索引i
2*i
- 索引
的右子级位于索引i
2*i+1
- 索引
的父级位于索引i
i/2
0索引的表示:
- 索引
i
的左子级位于索引2*i+1
- 索引
i
的右子级位于索引2*i+2
- 索引
i
的父级位于索引(i-1)/2
上图中的每个区间[i,j]
表示区间数据[i,j]
中所有元素的总和。根节点将表示整个数据[]的总和,即sum(0,6)
。它的两个子项将表示sum(0,3)
和sum(4,6)
等等
seg_tree[]的长度,MAX_LEN,将是(如果n=数据长度[]
):
2*n-1
当n是2的幂时
2*(2^(log_2(n)+1)-1
当n不是2的幂时
从数据[]构建seg_树[]:
在这种情况下,我们将假设一个0-indexed
结构。索引0将是树的根,初始数组的元素将存储在叶子中
data[0…(n-1)]
是初始数组,seg_-tree[0…MAX_-LEN]
是数据[]的段树表示形式。更容易理解如何从伪代码构造树:
build(node, start, end) {
// invalid interval
if start > end:
return
// leaf nodes
if start == end:
tree[node] = data[start]
return
// build left and right subtrees
build(2*node+1, start, (start + end)/2);
build(2*node+2, 1+(start+end)/2, end);
// initialize the parent with the sum of its children
tree[node] = tree[2*node+1] + tree[2*node+2]
}
这里,
[开始,结束]
表示数据[]中要形成段树表示的间隔。最初,这是(0,n-1)
- 节点表示seg_树[]中的当前索引
我们通过调用build(0,0,n-1)
开始构建过程。第一个参数表示根在seg_树[]中的位置。第二个和第三个参数表示要形成段树表示的数据[]中的间隔。在每个后续调用中,节点将表示seg_树[]和(start,end)的索引将表示seg_tree[node]存储总和的时间间隔
有三种情况:
start>end
是无效的间隔,我们只需从此调用返回
- 如果
start==end
,则表示seg_树[]的叶子,因此,我们初始化tree[node]=data[start]
- 否则,我们处于一个非叶的有效间隔中。因此,我们首先通过调用
build(node,start,(start+end)/2)
来构建此节点的左子树,然后通过调用build(node,1+(start+end)/2,end)
来构建右子树。然后通过子节点的总和初始化seg_tree[]中的当前索引
对于总和(i,j):
我们需要检查节点处的间隔是否与给定间隔(i,j)重叠(部分/完整),或者它们根本不重叠。以下是三种情况:
- 对于无重叠,我们可以简单地返回0
- 对于完全重叠,我们将返回存储在该节点上的值
- 对于部分重叠,我们将访问两个子项并递归地继续此检查
假设我们需要找到sum(1,5)
的值。我们按照以下步骤进行:
让我们以一个空容器(Q)为例,它将存储感兴趣的间隔。最终,所有这些范围都将被它们返回的值替换。最初,Q={(0,6)}
我们注意到(1,5)并没有完全重叠(0,6),所以我们删除了这个范围