【问题标题】:Is there any way to efficiently reconstruct a collection based on a sequence of inserts/removals?有什么方法可以根据插入/删除序列有效地重建集合?
【发布时间】:2011-06-23 06:22:52
【问题描述】:

注意:下面的代码恰好是 C#,但实际上任何语言的答案都会对我有所帮助。

假设不是一个实际的集合(例如,List<T>),我有一个操作序列,每个操作看起来像这样:

struct ListOperation<T>
{
    public enum OperationType { Insert, Remove }

    public OperationType Type;
    public T Element; // irrelevant for OperationType.Remove
    public int Index;
}

有没有什么方法可以有效地根据一系列这样的操作“重建”一个集合?

特别是,我希望避免明显(低效)的实现,即基本上只为每个元素创建一个 List&lt;T&gt; 并调用 InsertRemoveAt——这两个操作都是 O(N) 次。


更新:假设操作的“序列”实际上是一个具体的集合,其计数是已知的,并且可以通过索引随机访问(例如,像 ListOperation&lt;T&gt;[])。还假设 resulting 集合的实际计数是已知的(但实际上,无论如何,通过计算插入和删除,这在 O(N) 中是微不足道的)。还有其他想法吗?

【问题讨论】:

  • 是否可以从头到尾阅读或仅从头到尾阅读?
  • 顺便说一句,插入或删除可能是 O(1),最坏的情况是 O(n),但我想你知道
  • 这是一个真棒的问题。我今天没有 + 投票,但我一定会在明天到来时尽快投票。
  • 有趣。主要困难在于Index 与创建操作(插入/删除)时列表的当前状态有关。
  • @Rune FS:操作本身在任一方向上都是可读的。是的,我知道插入/删除 可能 很快;这是你提到的我试图避免的 O(n) 最坏情况。 @templatetypedef:谢谢;)我昨天在做一个项目时遇到了这个挑战,我个人觉得它真的很有趣——认为把它带到 SO 是个好主意! @Matthieu M.:是的,这似乎确实是个大问题。我希望有一个聪明的方法来解决它,但是......无论如何,看起来我已经有了一些想法。

标签: performance algorithm data-structures collections language-agnostic


【解决方案1】:

我认为您可以通过使用索引平衡二叉树(每个节点存储其左右节点数的二叉树)在 O(n lg n) 中做到这一点。使用这种结构,您可以通过遍历树以找到新元素所属的位置,然后进行任何必要的修复以保持平衡条件(例如,如果是红黑树,你会做红黑树修复)。

鉴于此设置,您可以在 O(n lg n) 中将所有操作重放为像这样的树结构,因为每个单独的操作最多需要 O(lg n) 才能完成。一旦你有了树,你就可以对元素进行中序遍历以使它们以正确的顺序返回,并且可以在 O(n) 时间内将所有值附加到结果列表中,用于 O(n lg n)。

我会更多地考虑这个问题,看看我是否能想出一种在线性时间内做到这一点的方法。与此同时,这至少表明可以在次二次时间内做到这一点。

【讨论】:

  • 不要考虑所有项目的线性时间,如果你能做到这一点,你可以在线性时间插入它们,在删除每个项目时你可以输出它(打印它)并删除使用 removeat(1), removeat(2), ... 所以你将在线性时间内输出排序列表,但这是不可能的(对于比较基本方法,你的插入应该有某种比较来填充平衡二叉树)。
  • @Saeed- 我不明白你在说什么。这不会打破 O(n lg n) 的排序障碍,也不会尝试对任何东西进行排序。你能澄清你的担忧吗?
  • 您是否会说您希望如何在 O(log(N)) 中插入项目以及如何在没有比较方法的情况下在 O(log(n)) 中插入平衡树?如果您使用比较方法,您可以按排序顺序输出您的项目,并且如果您有一些 O(x) 方式来插入或删除树以及平衡树(这是 O(x) 对于 n 项的总和是 O(n) ) 你可以通过删除 O(n) 中的项目来对它们进行排序,我不是说你对它们进行排序,但我是说平衡树的构建需要比较方法,你使用的任何方法都可以用于排序。
  • @Saeed- 啊,我明白你在说什么。您实际上不需要比较来平衡树;如果您考虑一下,即使节点本身没有任何特定的排序顺序,任何树都可以是红/黑树。这个想法是采用红/黑树的形状,并使用它来按顺序存储一系列(未排序的)值,以这样的方式存储它们,以便中序遍历访问它们所需的顺序。 CLRS 对此进行了讨论。顺便说一句,我不打算让这个系统在 O(n) 时间内工作……我希望这需要一些新的东西。
  • 这个结构有个名字:订单统计树。
【解决方案2】:

我有一种预感,可能存在 O(n) 算法。

第 1 步:

对索引进行数字化基数排序。花费 O(n) 时间。如果从 LSB 端完成,这是一个稳定的排序。

第 2 步:

假设有索引为 i 的操作,但没有未完成的索引较小的操作。然后我们可以以正确的顺序在索引 i 处重放操作。具体来说,我不清楚“插入”和“删除”操作在做什么。最坏的情况是 O(n lg n) 与二叉树的思想,但也许重播可以在 O(n) 中完成,因为它是本地的。

第三步:

将第 2 步提升到一个归纳论证,作为正确性的证明。在索引 i 处的步骤之后,需要维护一个不变量和一个较短的操作列表,因此通过归纳,...(详细信息)...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-17
    • 2014-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多