什么是保持累积值的良好数据结构?

时间:2009-05-28 20:23:51

标签: algorithm data-structures

我正在寻找一种在访问保持累积值的集合时非常有效的想法,概念或经过验证的数据结构。

示例可以更好地说明我的需求:

我有一个值列表(2,3,5)。 查看累积值时,此列表将为(2,5,10)。

我现在将在列表的开头添加1并获得(1,2,3,5)和累积术语(1,3,6,11)。

我只需要查看累积值,我对1,2,3,5一点也不感兴趣。我需要能够快速插入位置,移除一个位置,所有这一切都应该快速更新累积数组(理想情况下不需要遍历整个数组并重新计算值。

任何想法或提示?

@Kristo(太长时间不能放入评论):为了澄清为什么负数会使总和值无意义,请按照这个例子。

插入1后跟-1。总和是1比0。 (1,-1)//(1,0) 插入3,然后插入-3。总和是3然后是0。 (1,3,-1,-3)//(1,4,3,0) 插入2,然后插入-2。总和是2然后是0。 (1,3,2,-1,-2,-3)//(1,4,6,5,3,0)

如果我的“神奇数字”是4总额,则不会告诉我是否已经超过它。

PS:主要原因是能够判断我是否超过了某个值以及链中的哪个位置。

8 个答案:

答案 0 :(得分:5)

我能想到的唯一优化就是对累积列表进行“懒惰”评估。

除了您的源值列表之外,还要跟踪累积列表中准确的最高位置数。如果您需要一个高于该数字的数字,那么您可以走上列表,更新值和索引。

idx  values       cumulative    operation
 3   (2,3,5)      (2, 5, 10)
 0   (1,2,3,5)    (X,X,X,X)     insert 1 at 0 
 3   (1,2,3,5)    (1,3,6,X)     look for value over 5     
 3   (1,2,3,5,4)  (1,3,6,X,X)   insert 4 at 4 

如果当然,如果您通常在列表的早期添加项目,这对您不会有很多好处....

答案 1 :(得分:4)

答案 2 :(得分:4)

使用二叉搜索树,其中包含节点包含其子树总和的附加属性。所有操作仍然是O(lg n)。要插入或删除值,请执行常规过程,并更新所有父项的总和。获得总和就像找到包含元素的节点并返回其总和减去其子项的总和一样简单。

答案 3 :(得分:3)

在C#中,我会将所有实际值保留在列表中,并使用自定义迭代器来遍历累积值。

你只会重新计算到迭代器告诉你已超过限制的程度(显然,你必须为此编码)。

我认为值是你可以添加/删除而不进行任何计算,直到迭代列表(我认为你无论如何都要找到截止数字)。

答案 4 :(得分:1)

我看到两种简单的方法,都使用基本数据类型 - 列表。

  1. 保留原始列表,并重新计算每次更改的累积次数。

  2. 仅保留累积列表,只使用以下函数添加或删除:

    • 添加(项目,位置默认为列表的结尾)将从位置 -1开始添加项目的值。
    • 删除(位置)会计算原始值减去两个数字,然后在删除项目之前从列表的其余部分减少此数字。

    添加2:(2)将2加到空列表中。

    添加3:(2,5)在列表末尾将3添加到前一个元素(2)。

    添加5:(2,5,10)在列表末尾将5添加到前一个元素(5)。

    在开始时添加1:(1,3,6,11)在列表的开头添加1,并递增1直到结束(没有先前的元素)。

    在第二个位置添加7:(1,8,11,14,19)增加7并增加7直到结束(没有先前的元素)。

    删除第3个位置( 11 ):( 1,8,3,8)获取值,删除它,将值添加到其余位置。

  3. 这种方式可以保持全部同步而不保留原始值。

答案 5 :(得分:1)

使用C ++术语,您可以使用std::list(在中间轻松插入/删除)或std::set(始终排序)获取数据,使用一个变量来保存总和吗?在每次插入/移除时,您都会根据需要修改总和。总和代表您可能的累积列表中的最高数字。只有当你破坏你的幻数时,你才需要做一些算法工作来找出你被破坏的地方。

<强>更新

根据您的新信息,我看不到很多可用的快捷方式。您需要经常从中间插入或删除,以便建议某种链表方法。您只需更新已更改的列表部分即可保存一些计算。让L为值列表,n为列表中的所需位置。要在x位置插入值n

  • x + L(n-1)
  • 位置插入值n
  • 在此新x
  • 之后的所有元素中添加n
  • 如果你破坏了你的幻数
  • 就停止

除了从所有后续值中减去之外,删除过程相同。这样,如果你在开头附近插入,你只会做很多工作。

答案 6 :(得分:1)

  • 您可以查看Binary Indexed Trees
  • 累积频率的数据结构
  • 您可以将值范围分解为固定位范围。防爆。 3个时间间隔:

    #define NUM (1<<24)  // max value in your data set
    #define BITS0 8
    #define BITS1 8
    int cum0[NUM >> (BITS0+BITS1)]; // sum of cum1
    int cum1[NUM >> BITS1]; // sum of count
    int count[NUM];
    
    int add(id, val) { // add a value
      cum0[id >> (BITS0+BITS1)] += val;
      cum1[id >> BITS1] += val; 
      count[id] += val;                     
    }
    
    int cumvalue(int id) { int cum = 0; // return cum value at index id         
      for(i = 0; i < (id >> (BITS0+BITS1));i++) cum += cum0[i]; i <<= BITS0;
      for(i = (id & ~((1 << (BITS0+BITS1))-1)) >> BITS1; i < (id >> BITS1); i++) cum+= cum1[i]; i <<= BITS1;
      for(i = id & ~((1 << BITS1) -1); i < id; i++) cum += count[i];            
      return cum;
    }
    

答案 7 :(得分:0)

使用 https://en.wikipedia.org/wiki/Fenwick_tree

这将预期运行时复杂度在元素数量上呈对数增长,而不是像在幼稚的实现中那样线性增长。