在O(1)中插入,删除,最大

时间:2010-08-08 20:20:53

标签: algorithm data-structures

有人能告诉我哪个数据结构支持O(1)中的插入/删除/最大操作?

8 个答案:

答案 0 :(得分:56)

这是一个经典的面试问题,通常会这样呈现:

  

设计类似堆栈的数据结构,在O(1)时间内执行push,pop和min(或max)操作。没有空间限制。

答案是,你使用两个堆栈:主堆栈和最小(或最大)堆栈。

因此,例如,在将1,2,3,4,5推入堆栈后,您的堆栈将如下所示:

MAIN   MIN
+---+  +---+
| 5 |  | 1 |
| 4 |  | 1 |
| 3 |  | 1 |
| 2 |  | 1 |
| 1 |  | 1 |
+---+  +---+

但是,如果您要推送5,4,3,2,1,堆栈将如下所示:

MAIN   MIN
+---+  +---+
| 1 |  | 1 |
| 2 |  | 2 |
| 3 |  | 3 |
| 4 |  | 4 |
| 5 |  | 5 |
+---+  +---+

对于5,2,4,3,1,你会得到:

MAIN   MIN
+---+  +---+
| 1 |  | 1 |
| 3 |  | 2 |
| 4 |  | 2 |
| 2 |  | 2 |
| 5 |  | 5 |
+---+  +---+

等等。

只有当最小元素发生变化时,如果已知这些项目是不同的,您还可以通过推送到最小堆栈来节省一些空间。

答案 1 :(得分:16)

以下解决方案使用O(1)额外内存和O(1)时间进行最大,推送和弹出操作。 保持变量max,它将跟踪任何特定时间的当前最大元素。 让我们利用这样一个事实:当更新max时,堆栈中的所有元素应该小于新的max元素。 当发生推送操作并且新元素(newElement)大于当前最大值时,我们将推送堆栈中的max + newElement并更新max = newElement。

当我们进行弹出操作并且我们发现当前弹出元素大于当前最大值时,我们知道这是我们更新堆栈以保持max + elem的位置。因此,要返回的实际元素是max和max = poppedElem - max。

例如。如果我们正在推动1,2,3,4,5,那么堆栈和最大变量将如下所示:

MAIN       Value of MAX
+---+      +---+
| 9 |      max = | 5 |
| 7 |      max = | 4 |
| 5 |      max = | 3 |
| 3 |      max = | 2 |
| 1 |      max = | 1 |
+---+      +---+

现在假设我们弹出一个元素,我们基本上会弹出,max元素(因为top> max)并将max元素更新为(top-max)

MAIN       Value of MAX
+---+      +---+
| 7 |      max = | 4 | = (9-5)
| 5 |      max = | 3 |
| 3 |      max = | 2 |
| 1 |      max = | 1 |
+---+      +---+

现在我们假设我们正在推动数字5,4,3,2,1,堆栈看起来像:

MAIN       Value of MAX
+---+      +---+
| 1 |      max = | 5 |
| 2 |      max = | 5 |
| 3 |      max = | 5 |
| 4 |      max = | 5 |
| 5 |      max = | 5 |
+---+      +---+

当我们弹出时,顶部的堆栈会弹出顶部< max和max保持不变。

以下是每个操作的伪代码,以便更好地了解。

Elem max;
void Push(Elem x){
    if x < max :
        push(x);
    else{
        push(x+max);
        max = x;
    }
}
Elem Pop(){
    Elem p = pop();
    if |p| < |max|:
        return p;
    else{
        max = p - max;
        return max;
    }
}
Elem Max(){
    return max;
}

push和pop是正常的堆栈操作。希望这会有所帮助。

答案 2 :(得分:14)

@ KennyTM的评论指出了一个重要的缺失细节 - 插入位置,并从中删除。所以我假设你总是希望只从堆栈的一端插入和删除。

插入(推送)和删除(弹出)是O(1)。

要在O(1)中获取Max,请使用额外的堆栈来记录与主堆栈对应的当前最大值。

答案 3 :(得分:3)

如果您只使用比较,则很难找到这样的数据结构。

例如,您可以插入n个元素,获取最大值,删除最大值等,并可以在O(n)时间内对数字进行排序,而理论下限为Omega(nlogn)。

答案 4 :(得分:0)

下面的程序跟踪堆栈中的最大元素,以便顶部指针在堆栈中给出最大值的任何时间点: 所以,max将是O(1),我们可以通过max [N]

找到max
ITEM   MAX

+---+  +---+
| 1 |  | 1 |
| 10|  | 10|
| 9 |  | 10|
| 19|  | 19| <--top
+---+  +---+

Java程序:

public class StackWithMax {

private int[] item;
private int N = 0;
private int[] max;

public StackWithMax(int capacity){
    item = new int[capacity];//generic array creation not allowed
    max = new int[capacity];
}

public void push(int item){
    this.item[N++] = item;
    if(max[N-1] > item) {
        max[N] = max[N-1];
    } else {
        max[N] = item;
    }
}

public void pop() {
    this.item[N] = 0;
    this.max[N] = 0;
    N--;
}

public int findMax(){
    return this.max[N];
}
public static void main(String[] args) {
    StackWithMax max = new StackWithMax(10);
    max.push(1);
    max.push(10);
    max.push(9);
    max.push(19);
    System.out.println(max.findMax());
    max.pop();
    System.out.println(max.findMax());


}

}

答案 5 :(得分:0)

有些人已经指出,这个问题缺乏一些信息。您没有指定插入/删除,也没有指定我们正在处理的数据的性质。

一些可能有用的想法:你说,

  

在O(1)

中插入/删除/最大操作

请注意,如果我们可以在O(1)中插入,删除和查找maximun,那么我们可以使用这种hipotetical技术在O(n)中进行排序,因为我们可以插入n个元素,然后取max / delete我们把它们全部排序了。事实证明,没有基于比较的排序算法可以排序小于O(nlogn),因此我们知道没有基于比较的方法可行。事实上,其中一种最快的已知方法是Brodal队列,但它的删除时间超过O(1)。

也许解决方案就像基数树一样,所有这些操作的复杂性都与密钥长度有关,因为选择了密钥量。仅当它们允许您将密钥长度限制为某个其他数字时才有效,因此您可以将其视为常量。

但也许这不是通用的东西。另一种解释是插入/删除是经典堆栈。在这种受限制的情况下,您可以使用Can Berk Güder给您的双重堆栈解决方案。

答案 6 :(得分:0)

最好的是: 插入O(1) 在O中删除(登录) O(1)中的最大值/最小值

但是要做到这一点,insert函数必须创建一个链接链,并且您还需要一个额外的线程。好消息是此链接链函数也可在O(1)中使用,因此不会更改insert的O(1)。

删除功能不会中断链接链。

如果删除目标是最大或最小,则删除将在O(1)中执行

数据结构是avl树和链表的组合。

真正删除的本质是无法使它在O(1)中起作用。使用O(1)delete的哈希表没有可容纳所有输入的能力。

答案 7 :(得分:-1)

哈希表可能支持在O(1)中插入/删除,但没有关于最大值的线索。你可能需要以某种方式自己跟踪它。

相关问题