有人能告诉我哪个数据结构支持O(1)中的插入/删除/最大操作?
答案 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]
找到maxITEM 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)中插入/删除,但没有关于最大值的线索。你可能需要以某种方式自己跟踪它。