【问题标题】:Insert element at position i and return info based on i first elements在位置 i 插入元素并根据第 i 个元素返回信息
【发布时间】:2015-12-14 20:28:54
【问题描述】:

假设我有一个整数列表:

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 的程序。以下是将n 的位置i 插入List 然后返回i 第一个数字的总和:

  1. 比较最后一个插入位置ki。如果它更低,则为每个j: k < j < i 计算sum[j],如下所示:sum[j] = sum[j-1] + List[j] - O(n)
  2. 找到位置i - O(n)
  3. i 位置插入n,存储k = i - O(1)
  4. 计算并返回sum[i] = sum[i-1] + n - O(1)

这是否可以更有效地完成,也许使用不同的数据结构?在 O(logn) 中可能吗?如果是那怎么办?

【问题讨论】:

  • 乍一看,这听起来像是 Binary Indexed Tree 的工作
  • n 元素的总和将花费 O(n),但使用 std::vector<int> 会更快。对于std::vector<int>,找到该位置是O(1)。你可以用它换取 O(n)。在您的短名单上使用std::vector<int> 会快很多。在大型数据集上,这将取决于您需要转移多少,但向量在其操作中就在后面。
  • 只是想提一下,位置 4 实际上是 1 所在的位置,因为数组中的位置从 0 开始计数

标签: c++ algorithm performance data-structures


【解决方案1】:

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

  1. 比较最后一个插入位置ki。如果低于,计算总和:O(n)
  2. 如果涉及某种搜索,则查找位置 i:O(1)O(n)。如果涉及搜索,它仍然会比std::list 快得多。
  3. 在位置i插入nO(n)
  4. 计算并返回sum[i] = sum[i-1] + n:O(1)

从算法/可扩展性的角度来看,这可能看起来并不好,但由于算法复杂性,我们通常会看到可观的性能改进。这是由于参考的局部性(特别是空间局部性)。

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

std::list,当与std::allocator 一起使用时(尤其是在并非所有节点都同时分配的上下文中),由于它缺乏空间局部性,往往会引发大量缓存未命中(也部分地,由于列表指针的开销减少了可以放入缓存行的元素数量,在这种特殊情况下,基本上因为我们需要每个微不足道的整数两个列表指针。

请注意,在针对您的特定问题进行调整的标准库之外进行冒险时,可能存在更优化的解决方案,如另一个不错的答案中所述。深入研究较低级别细节的另一个角度是寻找您自己的自定义分配器,它可以真正帮助几乎任何类型的链接结构。这个答案侧重于香草 C++。在处理顺序容器时,vector 通常是你最好的选择(除非有其他强有力的理由),因为它是连续的、缓存友好的表示。

【讨论】:

    【解决方案2】:

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

    这项工作也可以用段树来完成,但是由于二叉索引树的实现要简单得多,我建议使用二叉索引树。

    【讨论】:

      猜你喜欢
      • 2015-11-17
      • 2017-08-07
      • 1970-01-01
      • 1970-01-01
      • 2021-12-04
      • 1970-01-01
      • 1970-01-01
      • 2021-10-17
      • 1970-01-01
      相关资源
      最近更新 更多