数据设计在O(1)中查找插入删除和getMin的问题

时间:2012-05-07 09:50:10

标签: algorithm data-structures hash

如标题所述,我需要定义一个数据结构,只需O(1)时间进行插入删除和getMIn时间....没有空间限制.....

我搜索了SO的相同,我发现的所有内容都是在O(1)时间插入和删除....即使是堆栈也是如此。我看到上一篇文章在堆栈溢出中他们所说的就是哈希......

我在O(1)时间内对getMIn进行了分析,我们可以使用堆数据结构 为了在O(1)时间插入和删除我们有堆栈......

所以为了实现我的目标,我认为我需要调整heapdatastructure和stack ... 我将如何在这种情况下添加散列技术...

如果我使用哈希表,那么我的哈希函数应该如何分析哈希方面的情况...任何好的参考将被赞赏...

6 个答案:

答案 0 :(得分:2)

如果您最初假设插入和删除是O(1)复杂性(如果您只想插入顶部并从顶部删除/弹出然后堆栈工作正常)那么为了让getMin返回在某种程度上你需要以恒定时间存储最小值的最小值。如果你只是有一个成员变量跟踪min,那么如果它被从堆栈中删除会发生什么?您需要下一个最小值,或相对于堆栈中剩余的最小值。要做到这一点,你可以让你的元素在一个堆栈中包含它认为最小的元素。堆栈在代码中由链表表示,因此链表中节点的结构看起来像这样:

struct Node
{
  int value;
  int min;
  Node *next;
}

如果您查看示例列表:7-&gt; 3-&gt; 1-&gt; 5-&gt; 2。让我们来看看如何构建它。首先你输入值2(到空堆栈),这是min,因为它是第一个数字,跟踪它并在构造它时将它添加到节点:{2,2}。然后你将5推到堆栈上,5> 2,所以min是相同的推动{5,2},现在你有{5,2} - &gt; {2,2}。然后你按1英寸,1 <2,所以新的分钟是1,按{1,1},现在它是{1,1} - &gt; {5,2} - &gt; {2,2}等等到最后你有:

{7,1} - GT; {3,1} - GT; {1,1} - GT; {5,2} - GT; {2,2}

在这个实现中,如果你弹出7,3和1你的新分钟将是2应该是。并且您的所有操作仍处于持续时间,因为您只是向节点添加了比较和另一个值。 (你可以使用类似C ++的peek()或只是使用指向列表顶部的指针来查看堆栈的顶部并抓住那里的min,它会给出你在恒定时间内的堆栈最小值。

这种实现的权衡是你的节点中有一个额外的整数,如果你在一个非常大的列表中只有一两分钟,那就浪费了内存。如果是这种情况,那么您可以在单独的堆栈中跟踪分钟,只需将您要删除的节点的值与此列表的顶部进行比较,如果匹配则将其从两个列表中删除。要记住更多的事情,所以这取决于具体情况。

免责声明:这是我在这个论坛的第一篇文章,所以如果它有点令人费解或罗嗦,我很抱歉。我也没有说这是&#34;一个真正的答案&#34;但它是我认为最简单的并且符合问题要求的那个。总是存在权衡取决于具体情况,需要采用不同的方法。

答案 1 :(得分:2)

这是一个设计问题,这意味着他们希望看到您可以多快地扩充现有的数据结构。

从你所知道的开始:

  • O(1)更新,即插入/删除,正在尖叫hashtable
  • O(1)getMin也是尖叫哈希表,但是这次订购了。

在这里,我提出了一种方法。你可能会找到你喜欢的其他东西。

  • 创建一个HashMap,称之为main,在哪里存储所有元素
  • 创建一个LinkedHashMap(java有一个),称之为mins跟踪最小值。
  • 第一次将元素插入main时,也将其添加到mins
  • 对于每个后续插入,如果新值小于mins地图头部的值,请使用等效于addToHead的内容将其添加到地图中。
  • main中删除元素时,也会将其从mins中删除。 2 * O(1)= O(1)
  • 请注意getMin只是偷看而不删除。所以只要看看mins
  • 的负责人

编辑:

Amortized algorithm

(感谢@Andrew Tomazos - Fathomling,让我们有更多乐趣!)

我们都知道插入哈希表的成本是O(1)。但事实上,如果你曾经构建过一个哈希表,你就知道必须保持表的大小加倍以避免溢出。每次将具有n个元素的表的大小加倍时,必须重新插入元素,然后添加新元素。通过这种分析,它会 似乎在哈希表中添加元素的最坏情况成本是O(n)。那么为什么我们说它是O(1)?因为并非所有元素都是最坏的情况!实际上,只有出现加倍的元素才会出现最坏情况。因此,插入n个元素需n+summation(2^i where i=0 to lg(n-1)),这会n+2n = O(n),因此O(n)/n = O(1) !!!

为什么不将相同的原则应用于linkedHashMap?无论如何你必须重新加载所有元素!因此,每当您将main加倍时,将main中的所有元素也放入分钟,并在mins中对它们进行排序。然后对于所有其他情况按上述步骤进行(子弹步骤)。

答案 2 :(得分:0)

散列表允许您在O(1)中插入和删除(堆栈不是因为您不能在堆栈中有孔)。但是你也不能在O(1)中使用getMin,因为排序你的元素不能比O(n * Log(n))(它是theorem)快,这意味着O(Log(n) ))每个元素。

你可以保持指向min的指针,使得getMin在O(1)中。这个指针可以很容易地更新插入,但不能删除min。但根据您使用删除的频率,这可能是一个好主意。

答案 3 :(得分:0)

严格地说,你所说的问题实际上是不可能的,但请考虑以下因素:

给定类型T对该类型的所有可能元素进行枚举,使得值i小于值j iff T(i)&lt; T(j)的。 (即按顺序编号T类型的所有可能值)

创建该大小的数组。

制作数组的元素:

struct PT
{
   T t;
   PT* next_higher;
   PT* prev_lower;
}

在数组中插入和删除元素,维护双链表(按索引顺序,因此按顺序排序)存储

这将为您提供常量getMin和delete。

对于插入,你需要在常量时间内找到数组中的下一个元素,所以我会使用一种基数搜索。

如果数组的大小是2 ^ x则保持x“跳过”数组,其中数组i的元素j指向要索引的主数组的最近元素(j <&lt;&lt; i)。

然后,这将始终需要固定的x次查找来更新和搜索,这样可以提供恒定的时间插入。

这使用了指数空间,但问题的要求允许这样做。

答案 4 :(得分:0)

您可以使用 trie 。 trie对于插入,删除和getmin都具有O(L)复杂度,其中L是您正在寻找的字符串(或其他)的长度。对于n(元素数量),它具有恒定的复杂性。

但它需要大量内存。由于他们强调“没有空间限制”,他们可能正在考虑一个特里。 :d

答案 5 :(得分:-1)

在您的问题陈述中“插入和删除O(1)时间我们有堆栈......” 所以我假设删除= pop() 在这种情况下,使用另一个堆栈来跟踪最小值

ALGO: 堆栈1 - 正常堆栈;堆栈2 - 最小堆栈

插入

推送到堆栈1。 如果堆栈2为空或新项目&lt; stack2.peek(),也推送到堆栈2

目标:在任何时候,stack2.peek()应该给你min O(1)

删除

来自堆栈1的

pop()。 如果popped元素等于stack2.peek(),则来自堆栈2的pop()