Optimization 优化整数数组的和(i,j)和更新(i,值)

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

给定一个庞大的整数数组,优化函数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,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),所以我们删除了这个范围