【问题标题】:O(1) Delete operationO(1) 删除操作
【发布时间】:2012-03-21 23:30:46
【问题描述】:

是否有任何数据结构或现有数据结构的变体为删除操作提供 O(1) 或恒定时间复杂度?

我知道哈希表可以做到。但是我正在修改哈希表,我们可以在不经过所有存储桶的情况下获取所有键,为此我将每个键存储在另一个链表中,并在我将其添加到哈希表时。所以我可以快速拿到所有的钥匙。

【问题讨论】:

  • 很难知道您是否正在寻找特定的元素删除[元素作为输入]?最小删除?第一个/最后一个元素删除?
  • @amcnabb:这是他们在采访中问我的:(
  • “但是我正在修改哈希表,我们可以在其中获取所有密钥而无需遍历所有存储桶”这听起来不标准。您能否发布有关如何完成此任务的详细信息?您选择删除哪些元素(根据@amit 的建议)?
  • @amit:我已经给出了我要删除的密钥。唯一的条件是它应该在恒定的时间内完成。
  • @SumitLonkar 从您的问题和 cmets 我了解到您知道密钥,并且您已经拥有哈希表实现,其中包含 O(1) 删除。并且您想在 O(1) 时间内从表中删除该键。那么问题是什么?

标签: data-structures time-complexity


【解决方案1】:

HashSet 呢?它们为添加/删除/包含/大小提供恒定的时间性能。

在java中称为HashSet

【讨论】:

  • 发生碰撞时它们不会随时间变化吗? (当然你想避免那些......但是是的)。
  • 是的,但冲突通常意味着你要么有一个糟糕的哈希函数(可修复),要么你有一个巨大的数据集(其他数据结构也会有同样的问题)。不确定 hashSet,但是对于 hashMaps,在发生冲突时,新值会附加到一个列表中,然后提供 O(n) 的添加/删除运行时(仍然不算太糟糕......)。
  • @DKT 大多数可靠的哈希表实现(包括 Java HashSet)会自动调整表的大小,以便负载因子大致保持不变。因此,即使使用大型数据集,整体运行时间仍然摊销 O(1)。
  • @James:是的,他们在发生碰撞时确实会自行调整,但据我所知,他们只是将存储桶的数量翻了一番,一切都会重新散列......
【解决方案2】:

我不确定我是否在关注 - 看来您想维护两个元素的链表,以便您可以快速迭代它们 - 仍然得到 O(1) - 所以您使用 HashSet。

你在正确的轨道上。在java中,您所描述的称为LinkedHashSet。

这个想法是:你有2个数据结构:

  1. 元素的LinkedList [双向链表]
  2. HashMap:Elements->Nodes - 哈希映射中的每个键都映射到链表中的对应节点。

使用此 DS,您可以获得以下操作:

add(x):
  if map.containsKey(x):
    return
  list.addLast(x)
  map.put(x,list.getLastNode()) 

delete(x):
  if (map.containsKey(x) == false):
    return
  list.deleteNode(map.get(x))
  map.delete(x)

请注意,添加和删除都是 O(1),它们只在地图和列表上执行最终数量的 O(1) 操作。

【讨论】:

  • 这里最大的问题是list.deleteNode()的实现,不是吗?
  • @FrankFarmer:在双向链表中相当容易,你得到实际的节点作为输入,所以你所要做的就是node.next.prev = node.prev和node.prev.next = node.next[和delete node]。 [当然你需要先检查节点是头部还是尾部-但仍然可以在O(1)中完成]
  • @amit:但是你必须再次遍历双向链表才能找到要删除的键,这是 o(n) 最坏的情况......
  • @SumitLonkar 没有。你可以使用 hashmap 在 O(1) 中找到对确切节点的引用!不需要在删除和插入中遍历列表
【解决方案3】:

是的,有几个。删除堆栈的顶部元素是 O(1),这是有效的,因为您只能访问堆栈的顶部。哈希表还对表的任何元素进行了分期 O(1) 删除。

【讨论】:

  • 删除该对象是 O(1) 或从堆栈中删除它是 O(1) (想想看,我想知道他问的是哪一个?)
  • @James 我的意思是从堆栈中删除,但是从任何数据结构中“删除”意味着从数据结构中删除。真正删除数据(即使数据对其他代码不可用)是特定于语言的。但是,此问题并未指定特定语言的标签。
  • 所以正如您所提到的,在这种情况下,您不必遍历它的任何数据结构都将是 O(1)?
  • @James 如果你根本不需要迭代,那么删除是 O(1)。但是,有很多情况下迭代是隐藏的。例如,要从数组中删除一个元素,您必须移动数组中所有后面的元素,因此您实际上是在迭代它。来自 FP 的经典链表没有这个问题:如果你用链表实现堆栈,你只需将头指针向前设置一个元素即可删除堆栈的头部。恒定时间。
【解决方案4】:

您是否正在寻找该 O 表示法的选择性(每个对象)删除?我的想法是,如果这是 X 时间所需的大量对象集合,但在那之后它们都被删除了,那么你可以将一个内存池标记为“空闲”,并理解从它分配的所有内存现在都无效,会工作(在为游戏卸载关卡以准备加载新关卡时使用此功能。)

内存池只是一个特定大小的内存块,通常用于特定目的。最困难的部分是为其编写分配例程,并且可能是解除分配。但是,当您完全使用该池时,您可以简单地重置跟踪信息以有效地“擦除”其所有数据并使其再次免费/可用。

【讨论】:

    【解决方案5】:

    当然有,但是所有警告之母。

    一个简单的节点数组,具有deleted 标志的已删除节点具有恒定的删除时间,但需要注意的是,它对所有其他操作的时间要求非常差。

    编辑

    答案最初谈到了一个 sorted 数组,但这是错误的单词选择(对不起,非英语母语)。我的意思是 indexed 就像在数组的经典案例中一样(想想 C) - 本质上意味着在访问数组元素时没有隐藏的 O(>1) (设置删除的标志)。

    还有一点:我说的只是删除,不是搜索和删除

    【讨论】:

    • 这个例子并不成立。要找到要删除的元素,您需要执行二进制搜索,这需要日志时间。是的,一旦找到元素,设置标志是一个恒定时间的操作,但无论列表是否排序都是如此。
    • @AdamMihalcin 问题不询问查找元素,它询问删除它。您需要首先找到它的假设是合理的,但不一定正确:如果在迭代数组时做出删除元素的决定是错误的。
    • @hvd 哎呀。我的错。我正在删除错误的评论(无法编辑),我只会发布有用的部分。
    • 我之前评论的有用部分:找到元素后删除 is 很便宜,但是这个答案明确地调用了一个排序列表。除非您关心在列表中查找元素,否则为什么要指定必须对列表进行排序?
    • @AdamMihalcin 我之前评论的有用部分:好点:)
    【解决方案6】:

    LinkedHashSet 或 LinkedHashMap。 HashSet 内部使用 HashMap,所以如果你不想要一个键,你可以使用 Hashset。
    LinkedHashMap 维护一个双向链表,贯穿其所有条目。这个链表定义了迭代顺序,这通常是键被插入到映射中的顺序,而映射使您可以更快地搜索键。
    Java 定义的库类没有提供开箱即用的直接入队和出队功能,但只需对源代码进行少量修改,您就可以得到问题的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-13
      • 1970-01-01
      • 2011-01-10
      • 2023-03-08
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多