【问题标题】:Find the largest sum subarray from the given array using segment trees使用段树从给定数组中找到最大和子数组
【发布时间】:2013-10-31 16:36:09
【问题描述】:

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

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

【问题讨论】:

  • 如果有 n 个元素,如何在 O(log(n)) 时间内解决它?你必须阅读它们,这需要 O(n) 时间。
  • 我的意思是查询可以在 O(log(n)) 时间内回答。
  • 那么,查询是否会类似于“完全位于此范围内的最大总和子数组是多少?”
  • 是的,如果我们提出疑问:左右。我们需要严格地在该范围内找到最大和子数组。

标签: arrays algorithm segment-tree


【解决方案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 是值。每次调用change 后,可以从p[1].bs(树的顶部)读取最大子数组的总和。

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

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

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

总结一下,分段树可以处理这个问题改变数据改变我们感兴趣的范围

【讨论】:

    【解决方案2】:

    根据我对贾斯汀回答的评论,您可以扩充标准段树以实现 O(log(n)) 查询时间和 O(n log(n)) 时间来构建树,即将所有 n 元素插入树中。

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

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

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

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

    我希望您能找出省略的细节。如果没有,请告诉我,我会给出更详细的解释。

    【讨论】:

    • O(n log(n)) 查询时间不满足问题要求;他想要一个带有 O(log n) 查询的段树。对分段树的查询本质上比 O(n) 方法更糟糕。
    • @Justin 每个查询的时间是 O(log(n)),O(n log(n)) 的时间是在段树中插入所有 n 个元素所需时间的界限跨度>
    • 您能否详细说明如何计算这 3 个值?当你说与 v 的子树对应的范围的左边界相邻的最大连续和是指 v 的左子树中的最大和以及对应的 right_value 的右子树中的最大和?
    • 不,让[a, b]为v's子树对应的范围。那么left_value[v] 是这个范围内的最大和前缀,即一个范围[a, i],其中a &lt;= i &lt;= b 的和是最大的。它回答了你的问题吗?
    • 不,我还是不明白。那么 right_value 和 max_value 会存储什么?
    猜你喜欢
    • 2016-10-16
    • 2022-06-12
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 2015-09-27
    相关资源
    最近更新 更多