使用段树从给定数组中查找最大的和子数组

时间:2013-10-22 17:06:13

标签: arrays algorithm segment-tree

我想找到给定数组中最大的和连续子数组。我知道使用Kadane算法使用动态规划的概念找到最大和连续子阵列方法的O(n)方法。

但是如果范围查询的数量非常大,则需要花费很多时间。有没有办法使用Segment-Trees来解决它,因为它是回答范围查询的首选方案,它在O(log(n))时间内解决。 谢谢。

2 个答案:

答案 0 :(得分:6)

根据我对Justin的回答的评论,您可以扩充标准的分段树以实现O(log(n))查询时间O(n log(n))时间来构建树,即将所有n个元素插入树中。< / p>

我们的想法是在每个节点v中存储不只是一个值,而是四个:

  
      
  1. max_value [v]:= v`s子树中的最大连续总和
  2.   
  3. left_value [v]:=与v子树对应的范围左边界附近的最大连续和
  4.   
  5. right_value [v]:=与v子树相对应的范围右边界附近的最大连续和
  6.   
  7. sum [v]:= v子树中所有元素的总和
  8.   

要为节点v执行更新操作,您必须重新计算max_value[v], left_value[v], right_value[v], sum[v]。这非常简单,我认为你可以自己解决这个问题 - 有几个案例需要考虑。

查询操作类似于基本段树中的查询操作。唯一的区别是,在这种情况下,您在计算结果时还必须考虑left_value[v]right_value[v] - 同样,还有一些容易考虑的案例。

我希望你能算出遗漏的细节。如果没有,请告诉我,我会给出更详细的解释。

答案 1 :(得分:1)

虽然@ pkacprzak的答案很好地描述了解决方案,但有些人可能更喜欢代码示例。

#include <iostream>
#define ll long long
using namespace std;
const int N=1<<17; // big power of 2 that fits your data

int n,k;
struct P {ll l, r, ts, bs;}; // left, right, totalsum, bestsum
P p[2*N];

ll maxf(ll a,ll b,ll c) {return max(a,max(b,c));}

P combine(P &cl,P &cr) {
  P node;
  node.ts = cl.ts + cr.ts;
  node.l = maxf(cl.l, cl.ts, cl.ts + cr.l);
  node.r = maxf(cr.r, cr.ts, cr.ts + cl.r);
  node.bs = maxf(cl.bs, cr.bs, cl.r + cr.l);
  return node;
}

void change(int k, ll x) {
  k += N;
  p[k].l = p[k].r = p[k].ts = p[k].bs = x;
  for (k /= 2; k >= 1; k /= 2) {
    p[k] = combine(p[2*k], p[2*k+1]);
  }
}

要在细分树中添加/更改值,请使用change(k, x)(每次调用O(log(n))),其中k是位置,x是值。每次调用p[1].bs后,可以从change(树的顶部)读取最大子阵列的总和。

如果您还需要找到子阵列的确切索引,您可以使用迭代查询在O(log(n))O(log^2(n))的二进制搜索中执行递归自上而下查询。

编辑:如果我们对给定子阵列的最大子阵列感兴趣,最好构建一个递归的自顶向下查询。参见:

https://www.quora.com/How-do-I-calculate-the-maximum-sub-segment-sum-in-a-segment-tree

总而言之,细分树可以通过更改数据和来处理此问题,并更改我们感兴趣的范围