Algorithm 什么是保存累积值的好数据结构?

Algorithm 什么是保存累积值的好数据结构?,algorithm,data-structures,Algorithm,Data Structures,我正在寻找一个想法、概念或经验证的数据结构,在访问保存累积值的集合时,它将非常有效 这个例子可以更清楚地说明我的需要: 我有一个值列表(2,3,5)。 当查看累积值时,该列表将为(2,5,10) 现在,我将在列表的开头添加1,得到(1,2,3,5)和累积项(1,3,6,11) 我只需要看看累积值,我对1,2,3,5一点也不感兴趣。我需要能够快速插入位置,删除位置,所有这些都应该能够快速更新累积数组(理想情况下不需要在整个数组中迭代并重新计算值) 有什么想法或提示吗 @克里斯托(太长了,不能在评论

我正在寻找一个想法、概念或经验证的数据结构,在访问保存累积值的集合时,它将非常有效

这个例子可以更清楚地说明我的需要:

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

现在,我将在列表的开头添加1,得到(1,2,3,5)和累积项(1,3,6,11)

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

有什么想法或提示吗

@克里斯托(太长了,不能在评论中说):为了澄清为什么负数会使总和变得毫无意义,请遵循这个例子

插入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:这样做的主要原因是能够判断我是否超过了某个值以及在链中的位置。

检查。

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

  • 保留原始列表,并重新计算每次更改的累积值

  • 仅保留累积列表,仅使用以下功能添加或删除:

    • Add(item,position默认为列表末尾)将添加从position-1开始的项的值
    • Delete(position)将计算原始值减去两个数字,然后在删除项目之前从列表的其余部分中减少该数字
    添加2:(2)将2添加到空列表中

    add3:(2,5)将列表末尾的3添加到前面的元素(2)

    Add 5:(2,5,10)将列表末尾的5添加到前面的元素(5)中

    在开始处添加1:(1,3,6,11)在列表的开始处添加1,并递增1直到结束(无优先元素)

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

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


  • 此方法将保持所有的同步,而不保留原始值。

    < P>使用C++术语,可以避开<代码> STD::列表< /C>(容易插入/删除中间)或<代码> STD::SET(总是排序)对于数据和一个变量来保存总和?在每次插入/移除时,您都会适当地修改总和。总和代表您的潜在累积列表中的最高数字。只有当您打破了神奇数字时,您才需要进行一些算法工作,以找出您打破的位置

    更新:

    根据您的新信息,我看不出有多少快捷方式可用。您需要经常从中间插入或删除,这表明了某种链表方法。只需更新列表中已更改的部分,即可节省一点计算量。让
    L
    作为值列表,让
    n
    作为值列表列表中所需的位置。要在位置
    n
    插入值
    x

    • 在位置
      n
      处插入值
      x+L(n-1)
    • x
      添加到此新
      n
    • 如果你打破了你的魔法数字,就停下来
    删除的过程与删除相同,只是从所有后续值中减去。这样,如果在开始附近插入,则只需做大量工作。

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

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


    我认为它的价值在于,在对列表进行迭代之前,您可以不进行任何计算就添加/删除列表(我认为您无论如何都需要这样做才能找到截止值)。

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

    除了源值列表之外,还要跟踪累积列表中准确的最高位置的数字。如果需要高于该数字的数字,则可以在列表中查找,更新值和索引

    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 idx值累积运算 3 (2,3,5) (2, 5, 10) 0(1,2,3,5)(X,X,X,X)在0处插入1 3(1,2,3,5)(1,3,6,X)查找大于5的值 3(1,2,3,5,4)(1,3,6,X,X)在4处插入4
    当然,如果您通常在列表的早期添加项目,那么这对您没有多大好处。…

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

    • 您可以查看累积频率的数据结构
    • 您可以将值范围拆分为固定的位范围。例如,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;
      }
      
      #定义NUM(1(位0+1)];//cum1之和
      int cum1[NUM>>位s1];//计数之和
      整数计数[NUM];
      int add(id,val){//添加一个值
      cum0[id>>(BITS0+BITS1)]+=val;
      cum1[id>>位s1]+=val;
      计数[id]+=val;
      }
      int cumvalue(int id){int cum=0;//返回索引id处的cum值
      对于(i=0;i<(id>>(BITS0+BITS1));i++)cum+=cum0[i];i>BITS1);i++)cum+=cum1[i];i使用a

      这将具有预期的运行时复杂性