C++ 在位置i处插入元素并返回基于第一个元素i的信息

C++ 在位置i处插入元素并返回基于第一个元素i的信息,c++,algorithm,performance,data-structures,C++,Algorithm,Performance,Data Structures,假设我有一个整数列表: 2, 1, 3, 1, 4, 2, 5, 3, 2 我希望能够在位置I插入一个新的整数。假设i是4,我想插入数字7。结果将是: 2, 1, 3, 7, 1, 4, 2, 5, 3, 2 插入后,我希望收到一些基于I和更低位置的数字的信息。例如,第一个i数字的总和。在这种情况下,它将是2+1+3+7=13 我希望能够一次又一次地重复这个过程 我用C++编写了一个程序,使用了 STD::List。下面是它在i位置插入n到List中,然后返回i第一个数字的总和的操作: 将上

假设我有一个整数列表:

2, 1, 3, 1, 4, 2, 5, 3, 2
我希望能够在位置
I
插入一个新的整数。假设
i
是4,我想插入数字7。结果将是:

2, 1, 3, 7, 1, 4, 2, 5, 3, 2
插入后,我希望收到一些基于
I
和更低位置的数字的信息。例如,第一个
i
数字的总和。在这种情况下,它将是
2+1+3+7=13

我希望能够一次又一次地重复这个过程

<>我用C++编写了一个程序,使用了<代码> STD::List。下面是它在
i
位置插入
n
List
中,然后返回
i
第一个数字的总和的操作:

  • 将上次插入位置
    k
    i
    进行比较。如果它较低,则对每个
    j:k
    计算
    sum[j]
    ,如下:
    sum[j]=sum[j-1]+List[j]
    -O(n)
  • 查找位置
    i
    -O(n)
  • i
    位置插入
    n
    ,存储
    k=i
    -O(1)
  • 计算并返回
    sum[i]=sum[i-1]+n
    -O(1)

  • 是否可以使用不同的数据结构来更有效地实现这一点?也许在O(logn)里?如果是,那么怎么做?

    如果您想要一个开箱即用的解决方案,而不需要滚动新的数据结构或使用第三方库,
    std::vector
    将是您的最佳选择。算法复杂性为:

    2, 1, 3, 7, 1, 4, 2, 5, 3, 2
    
  • 将上次插入位置
    k
    i
    进行比较。如果较低,则计算总和:
    O(n)
  • 查找位置i:
    O(1)
    O(n)
    如果涉及某种搜索。如果涉及到搜索,它仍将大大快于
    std::list
  • i
    位置插入
    n
    O(n)
  • 计算并返回
    sum[i]=sum[i-1]+n
    O(1)
  • 从算法/可伸缩性的角度来看,这似乎不是更好,但由于算法的复杂性,我们通常不会看到相当大的性能改进。这是由于参考的位置(特别是空间位置)

    由于在从缓存线中逐出之前可以访问多个相邻元素,因此机器可以非常快速地按顺序遍历连续数据
    std::vector
    对这一点非常有用,我们最终受益于它对上述4种情况的快速、连续、顺序访问

    std::list
    std::allocator
    一起使用时(尤其是在并非所有节点都一次分配的上下文中),往往会调用大量缓存未命中,因为它缺少空间位置(另一方面,部分原因是列表指针的开销减少了可以放入缓存线的元素数量,在这种特殊情况下,这主要是因为我们要求每个小整数有两个列表指针)


    请注意,当您在标准库之外冒险时,可能会有更多的最佳解决方案,这些库针对您的特定问题进行了调整,如另一个很好的答案中所述。深入研究较低级别细节的另一个角度是寻找您自己的自定义分配器,它可以真正帮助任何类型的链接结构。这个答案集中于在《香草C++》中,<>代码>矢量< /代码>通常是你的最佳选择(除非有其他强烈的理由)当处理顺序容器时,给定其连续的、缓存友好的表示。

    正如@andyg在评论中提到的,这是一项适合Fenwick树或二元索引树的工作。二元索引树可以在
    O(logn)
    中进行插入和更新,并在
    O(logn)中进行查询(从开始到索引的总和)
    。有一篇关于二叉索引树的非常好的文章


    同样,这项工作也可以用段树完成,但由于二元索引树的实现非常简单,我建议使用二元索引树。

    乍一看,这听起来像是一项针对a的工作,
    n
    元素的总和需要O(n),但使用
    std::vector
    时速度会更快。查找位置是O(1)对于
    std::vector
    。您可以用它来交换插入O(n)。在短列表中使用
    std::vector
    会快得多。在大数据集上,这取决于需要移位的程度,但向量在其操作中正好位于后面。我只想指出,位置4实际上是1所在的位置,因为数组中的位置从0开始计数