【问题标题】:Given a list L labeled 1 to N, and a process that "removes" a random element from consideration, how can one efficiently keep track of min(L)?给定一个标记为 1 到 N 的列表 L,以及一个从考虑中“移除”随机元素的过程,如何有效地跟踪 min(L)?
【发布时间】:2013-07-22 16:34:20
【问题描述】:

问题几乎在标题中,但说我有一个列表 L

L = [1,2,3,4,5]

min(L) = 1 这里。现在我移除 4。最小值仍然是 1。然后我移除 2。最小值仍然是 1。然后我移除 1。最小值现在是 3。然后我移除 3。最小值现在是 5,依此类推。

我想知道是否有一种好方法可以随时跟踪列表的最小值,而无需执行 min(L) 或扫描整个列表等。

实际从列表中删除项目会产生效率成本,因为它必须将其他所有内容移过来。每次重新排序列表也很昂贵。有没有办法解决这个问题?

【问题讨论】:

  • 如果列表按照上面的方式排序,而不仅仅是list[0]
  • 保留排序列表的副本,然后是 alist[0]?
  • 从列表中删除元素是有代价的,因为它必须将其他所有内容都移过来。我想知道是否有更好的方法可以使用静态数组来做事。
  • 可以有辅助数据结构吗?或者您是否正在尝试就地执行此操作?它必须是一个列表还是可以是其他东西?
  • @JohnSmith 我认为你搞砸了。如果列表中没有排序,并且您没有在其他地方保留列表的有序版本,那么每次更新都是 O(n)。使用辅助列表,删除是 O(1),插入是 O(n),使用辅助堆 O(log n) 对于两者。

标签: python algorithm list min


【解决方案1】:

要删除随机元素,您需要知道哪些元素尚未删除。

要知道最小元素,您需要对项目进行排序或扫描。

实现为数组的最小堆巧妙地解决了这两个问题。删除一个项目的成本是 O(log N),找到最小值的成本是 O(1)。这些项目连续存储在一个数组中,因此随机选择一个非常容易,O(1)。

最小堆被描述为on this Wikipedia page

顺便说一句,如果数据很大,您可以将它们留在原处,并将指针或索引存储在最小堆中,并相应地调整比较运算符。

【讨论】:

  • 最小堆本身就是这样做的。
  • 不正确。移除 smallest 项的成本是 O(lg n)。删除任意项目的成本是 O(n)。
  • 你可以删除一个元素,用它的最小子元素替换它,然后递归。它是 O(log N)。即,堆中的每个元素都可以被视为子最小堆的根。见:cs.stackexchange.com/questions/6990/deletion-in-min-max-heaps
  • 但是在你可以用任何东西替换它之前,甚至需要 O(n) 才能找到该项目。从根开始,你不知道它属于哪个子堆,所以你基本上必须遍历整个数据结构才能找到它。
  • 你不需要找它;我们通过最小堆数组中的位置随机选择它。
【解决方案2】:

Google 用于自平衡二叉搜索树。从初始列表构建一个需要 O(n lg n) 时间,查找和删除任意项目将花费 O(lg n)(而不是 O(n) 从简单列表中查找/删除)。最小的项目将始终出现在树的根部。

This question 可能有用。它提供了各种平衡二叉搜索树的几种实现的链接。使用哈希表的建议不适用于您的情况,因为它没有解决维护最小项目的问题。

【讨论】:

    【解决方案3】:

    这是一个需要 O(N lg N) 预处理时间 + O(lg N) 更新时间和 O(lg(n)*lg(n)) 删除时间的解决方案。

    预处理:

    第 1 步:对 L 进行排序

    第2步:对于每个项目L[i],映射L[i]->i

    第 3 步:构建二叉索引树或分段树,其中对于 L 的每 1

    查询类型删除:

    步骤 1:如果项目 x 被删除,则对数组 L(其中 L 已排序)进行二进制搜索或从映射中找到它的索引。设置 BIT[index[x]] = 0 并更新所有范围。运行时间:O(lg N)

    查询类型 findMin:

    第 1 步:对数组 L 进行二分搜索。对于每个中间值,从 1-mid 找到 BIT 上的总和。如果 BIT[mid]>0 那么我们知道一些 value

    段树也可以这样做。

    编辑:如果我没记错的话,可以使用链表在 O(1) 中处理

    【讨论】:

      【解决方案4】:

      如果排序 不符合您的最佳利益,我建议只在您需要进行比较的地方进行比较。如果您删除了不是旧最小值的元素,并且没有插入任何新元素,则不需要重新扫描必要以获取最小值。

      您能否向我们提供更多有关您正在尝试进行的处理的信息?

      评论答案:您不必计算 min(L)。只需跟踪其索引,然后仅在删除位于(或低于)旧索引时重新运行 min(L) 扫描(并确保相应地跟踪它)。

      【讨论】:

      • 我的列表很大,我正在从考虑中“删除”元素,直到列表为空,但我需要跟踪每个步骤的最小值。但是,每次执行 min(L) 或对列表进行排序的成本很高。
      • @JohnSmith 每次删除最小值时都运行它,正如我在回复中指出的那样,成本太高?
      • 您不能将索引保持在最小值,因为您必须考虑从索引低于当前最小值的元素中删除的元素。
      • 好吧,如果是这样的话,他不能只使用当前索引并重置(编辑:不一定“重置”而是更多,“监控”)基于索引数量的索引向左还是向右,并且保持跟踪即使它移动了?
      • 保持索引更新是微不足道的。如果删除索引左侧的项目,它会向左移动一步。如果您删除右侧的项目,则无需执行任何操作。
      【解决方案5】:

      您当前在移除最小值时重新扫描的方法是 O(1) 时间,预计每次移除都需要 O(1) 时间(假设每个项目都同样可能被移除)。

      给定一个包含 n 个项目的列表,需要以 1/n 的概率重新扫描,因此每一步的预期工作量为 n * 1/n = O(1)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-21
        • 1970-01-01
        • 2013-11-14
        • 2021-11-14
        • 2014-05-25
        • 2011-04-24
        • 2021-06-29
        • 1970-01-01
        相关资源
        最近更新 更多