36

Can someone tell me which data structure supports insert/delete/maximum operation in O(1)?

realnumber
  • 2,124
  • 5
  • 25
  • 33
  • 12
    Is this homework? – jtbandes Aug 08 '10 at 20:21
  • 11
    Insert at where? Delete from where? O(1) is amortized or exact? – kennytm Aug 08 '10 at 20:22
  • 2
    I don't think this is homework. –  Aug 08 '10 at 20:24
  • 7
    I guess it is a silly interview question. :) – Andras Vass Aug 08 '10 at 21:22
  • 1
    Side remark: [Van Emde Boas trees](http://en.wikipedia.org/wiki/Van_Emde_Boas_tree) allow insert, delete, maximum (and others) in O(log log n), which is *really* close (considering there's nothing between Theta(1) and Theta(log log n) in TM model). – sdcvvc Aug 08 '10 at 23:34
  • 1
    possible duplicate of [design a stack such that getMinimum( ) should be O(1)](http://stackoverflow.com/questions/685060/design-a-stack-such-that-getminimum-should-be-o1) – Can Berk Güder Aug 10 '10 at 20:18
  • see also http://stackoverflow.com/questions/4802038/ – sdcvvc Dec 23 '11 at 23:34
  • It is possible to do this with O(1) extra memory. Take a look at my answer. – tranquil Aug 29 '14 at 08:14
  • Although @Can Berk Güder's [answer](https://stackoverflow.com/a/3435998/301444) is right. But if we have space constraint, we can do much better even if the elements are not distinct. For more details please see my answer [Here](https://stackoverflow.com/a/16485020/301444) with implementation in `Java`. – Vasu May 10 '13 at 14:57

8 Answers8

58

This is a classical interview question, and is usually presented like this:

Devise a stack-like data structure that does push, pop and min (or max) operations in O(1) time. There are no space constraints.

The answer is, you use two stacks: the main stack, and a min (or max) stack.

So for example, after pushing 1,2,3,4,5 onto the stack, your stacks would look like this:

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

However, if you were to push 5,4,3,2,1, the stacks would look like this:

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

For 5,2,4,3,1 you would have:

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

and so on.

You can also save some space by pushing to the min stack only when the minimum element changes, iff the items are known to be distinct.

Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
  • 10
    -1: Same answer as Anurag's and does not actually answer the question (IMO). –  Aug 08 '10 at 20:52
  • i went to interview for last week some folks asked me this question, i suggested priority queue. your answer seems to be correct – realnumber Aug 08 '10 at 22:01
  • @Ram: Sorry, I disagree that this is the correct answer. (As you yourself do not know exactly what they were asking). The interpretation of constraining either the inserts or deletes is silly, IMO. Anyway, since this is your question, you are free to do as you please :-) –  Aug 08 '10 at 22:18
  • You can also do the space saving even without knowing items are distinct. On pop, only pop the min stack if popped value == minStack.Peek() and (mainStack.Empty || popped != mainStack.Peek()) (after pop). It makes for a more expensive pop, but still in constant time complexity. – Jon Hanna Aug 08 '10 at 23:15
  • @Moron: Agree. I don't think push/pop is equivalent to insert/delete. I think an infinite size random access storage (aka infinite size RAM) could do the job. To store integers, just set the corresponding bit to 1. For example, to store the elements [3, 4, 7], just set the 3rd, 4th and 7th bit in the infinite storage, resulting "01001100" in the 1st byte. Use another integer to store the maximum element (7 is stored in this case). Listing all the elements in the array is O(m) [m is the max. element], but inserting (setting the bit), deleting (unsetting the bit) and finding the maximum is O(1). – Siu Ching Pong -Asuka Kenji- Aug 09 '10 at 10:03
  • @Siu: When the model is not specified, I think we can assume a 'practical' RAM machine I suppose. You solution will work if the integers are bounded. The question does not mention integers, though. –  Aug 09 '10 at 14:31
  • 1
    @Moron: Since there is a limit on the characters in one comment, I supposed the solution for other types should be left for an exercise :). The question posted by Güder was pretty short. I don't think making no assumption at all is practical either. By the way, we can assume that the elements are of the type (or share the same superclass), or at least, comparable to each other. I treat this question as somehow similar to an "IQ Quiz" (or mind-breaker), since, as far as I know, it is impossible to construct such a data structure for "a normal computer" in a practical situation. – Siu Ching Pong -Asuka Kenji- Aug 09 '10 at 15:09
  • @Moron: Here goes my answer for the "exercise": For integers that fit in one CPU register, follow the solution I posted above. For integers that don't fit in one CPU register, treat them as objects (described below). For floating point numbers, just use the bit representation as if they're integers. For objects with unique integer identifiers, use the same algorithm above, but store the whole object in the "slot" instead of setting the bit. For objects with unique identifiers which are not integers, transform them into unique integers if possible. (I don't think this should be explained.) – Siu Ching Pong -Asuka Kenji- Aug 09 '10 at 15:26
  • @Moron: For objects that don't have transformable identifier, or don't have an identifier at all, use the whole object bytes (bits) as integers. – Siu Ching Pong -Asuka Kenji- Aug 09 '10 at 15:30
  • @Siu: One problem I see is with duplicates. Two different bytes of the objects might be different, but they might in fact be the 'same' object. Deleting would become a problem then. Anyway, I agree with you :-) –  Aug 09 '10 at 16:03
  • 1
    its an acceptable answer for this question. but the question itself has no practical purpose other than confusing candidates – realnumber Aug 09 '10 at 18:18
  • @Ram: All I can say is that you most likely misunderstood the interviewer. Sorry if that sounds rude. Maafi. The interpretation in this answer and Anurag's is pretty ridiculous, IMO and if the interviewer was looking for that, he is an idiot. The other interpretation actually makes the question of practical use. –  Aug 09 '10 at 18:25
  • Turns out this question was asked before, and answered by none other than Jon Skeet. See my comment on the question. For reference, I myself was once asked this question (and got confirmation that my answer was correct) at a Google interview. Jon also works at Google, so he might have asked/have been asked this question at interviews. – Can Berk Güder Aug 10 '10 at 20:21
  • 2
    @Can: No, it is not the same. The other question _explicitly_ states to design a _stack_ with push/pop/max being O(1). Stack isn't mentioned anywhere in this question. If you look at any standard text, questions like these (which ask for a datastructure instead of explicitly specifying one) imply insert(x), delete(x) and max. Not insert on top, delete from top and max (which IMO is a ridiculous interpretation). –  Aug 11 '10 at 14:57
  • How will it take O(1) in worst cases to delete an element? Say you delete the element at the bottom of the stack as the worst case. – Omkar Kulkarni Jul 24 '22 at 01:21
18

The following solution uses O(1) extra memory and O(1) time for max, push and pop operations. Keep a variable max which will keep track of the current max element at any particular time. Lets utilize the fact that when max is updated, all the elements in the stack should be less than the new max element. When a push operation occurs and the new element(newElement) is greater than the current max we push the max + newElement in the stack and update max = newElement.

When we are doing a pop operation and we find that the current popped element is greater than the current max then we know that this is place where we had updated our stack to hold max+elem. So the actual element to be returned is max and max = poppedElem - max.

For eg. if we are pushing 1, 2, 3, 4, 5 the stack and max variable will look like below:

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

Now lets say we pop an element, we will basically pop, max element(since top > max) and update the max element to (top-max)

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

Now lets say we are pushing numbers 5, 4, 3, 2, 1, the stack will look like:

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

When we pop, the top of stack is popped since top < max, and max remains unchanged.

Following is a pseudo code for each of the operation for better insight.

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 and pop are normal stack operations. Hope this helps.

tranquil
  • 499
  • 7
  • 13
  • 3
    Wonderful answer! This uses more than O(1) space, though - since each array slot now has to be able to hold max + value, it now must have an extra bit of space. It is equivalent to a solution in which each slot has a bit to indicate whether it increased the maximum and slots that did increase the maximum hold the previous maximum -- the maximum that existed when the value in that slot was pushed. This has the virtue of working on non-numeric types. – jbapple Feb 12 '17 at 09:21
  • Thanks! I agree with what you said. – tranquil Feb 14 '17 at 05:38
  • 1
    It doesn't seem to be working for negative numbers. For example, Push -6, maxElement is -6, then comes -4, you will push (-6)+(-4) = -10 and the new maxElement is -4. But -10<-4 – A_G Apr 14 '17 at 01:59
  • @AsthaGupta good observation. Small tweak to the pop function makes the algorithm work for negative cases as well. I've changed p < max to |p| < |max|. – tranquil Jun 26 '17 at 23:33
  • @AsthaGupta you just need to push 2*x - max, so if max is -6 and x is -4, you push -2 and -4 is now max, so when you pop the -2, it is greater than the max, so you set the max to 2 * -4 = -8 - -2 = -6 and return -4 (the max) – TRuhland Dec 30 '17 at 20:26
  • Shouldn't the return value of Pop() in else statement be `return p - max` instead of `return max`? @tranquil – NightFury13 May 07 '19 at 14:00
  • Thanks @jbapple. The Internet is littered with variation of this pseudo-O(1) space solution, but nobody analyzes the reduction in values that the stack can store. Also, I think the extra bit solution that you suggests is cleaner and easier to understand. – user1202136 Aug 27 '19 at 13:07
14

@KennyTM's comment points out an important missing detail - insert where, and delete from where. So I am going to assume that you always want to insert and delete only from one end like a stack.

Insertion (push) and Delete (pop) are O(1).

To get Max in O(1), use an additional stack to record the current max which corresponds to the main stack.

Anurag
  • 140,337
  • 36
  • 221
  • 257
  • +1: I guess this was the "usual" interview question or homework involving two stacks / stack with two values (current, max) and insert/delete is rather push/pop. – Andras Vass Aug 08 '10 at 20:32
  • @and: Why assume this is an interview question? Ram might well be just curious and not sure how changing the problem will help in that case. I would think if the order of inserts/deletes matters, OP would have mentioned that. -1: Till OP clarifies. –  Aug 08 '10 at 20:34
  • 1
    @Moron: because of the "homework" tag, the "which data structure supports" part - and I have already met this question worded misleadingly. :) Of course, as you have pointed out it could be that Ram is just curious. – Andras Vass Aug 08 '10 at 20:40
  • @and: What homework tag ;-)? If you look at the edits Ram never put the homework tag. It was added by someone else. –  Aug 08 '10 at 20:42
  • 1
    @Moron: the fact that I have already heard the exact same question from people who boasted with their smart puzzles that they give to job applicants was a strong indication for me that it is in fact an interview question. – Andras Vass Aug 08 '10 at 20:57
  • @andras: Just because is sounds similar to some other question, I think it is incorrect to assume that it is the same. If it was what Anurag/you assumed, I would say the question would have mentioned 'stack' somewhere! From the fact that it does not, I believe the question is about finding a DataStructure which can do insert/delete arbitrary/max operation in O(1) time. Anyway... OP can always clarify. –  Aug 08 '10 at 21:01
  • 3
    @Moron: to clarify: I have met this question with the same exact misleading wording. A guy told me that it is more interesting to watch for reactions. Applicant type #1 - think-outside-the-box guy: since the interviewer did not mention anything else, constrains delete/insert to last element, problem solved. Applicant type #2 - regular guy: goes on to explain how it is impossible and what the lower theoretical limit is with different data structures. Applicant type #3 - clueless. I believe I would be #2 as well without hints (like delete/insert is for the last element). :) – Andras Vass Aug 08 '10 at 21:12
  • @andras: Sorry, the stack max thing has been beaten to death already, and I would not consider it "out of the box". A linked list works too, for that matter. I could interpret delete to mean delete the min always and that could be made to work too. If the interviewer is looking to mislead with such wording, I think he is an idiot. Anyway, this conversation is pointless, and OP's clarification is all we need :-) –  Aug 08 '10 at 21:19
  • @Moron: I guess the more one thinks about, the more one finds this answer useful. It satisfies all the requirements set forth in the question. Namely, the question did not ask for deletes in arbitrary order. :) – Andras Vass Aug 08 '10 at 21:21
  • @Moron: Yeah, I know. :) I was just answering your question as to why I think that the most probable answer is this one. – Andras Vass Aug 08 '10 at 21:28
  • @andras: Have to disagree. In your interpretation (pick your own constraints on inserts and deletes) the question kind of becomes meaningless. Anyway, pardon me if I don't respond anymore, we are just talking while all we need is OP to tell us what the interpretation is :-) Edit: Ok I see our comments crossed :-) –  Aug 08 '10 at 21:33
  • 1
    "Insert where, delete from where". These questions are meaningless. The stated data structure defines operations insert(x), delete(x), top(). An implementation of these is free to store the elements *anywhere it pleases*. What matters is: 1) is the implementation correct?, and 2) are the bounds of the operations O(1), as required? Btw your answer is wrong, as others pointed out. – Dimitris Andreou Aug 09 '10 at 00:16
  • "is the implementation correct?" - thanks for pointing that out. I would have never guessed that requirement. The question in its current form is **vague**. Why? Because there is such a [machine](http://stackoverflow.com/questions/2489190/gravity-sort-is-this-possible-programatically/2527873#2527873) in my backyard that only sorts spheres of varying dimensions (magnitude). Insertion and deletion are O(1). Just drop the thing and take it out. Finding the maximum is again O(1). The machine tracks which sphere hit the base first. But I suppose the OP isnt looking for something like that. – Anurag Aug 09 '10 at 01:52
  • @Anu: Don't be silly. Do you really want OP to specify a model of computation each time a question is asked? Frankly, I think there is only one _reasonable_ interpretation of this question and you don't have it. (Just check questions like these in CLR/any algorithms text and see what the intent is). –  Aug 09 '10 at 14:27
3

If you are using only comparisons, you would be hard pressed to find such a data structure.

For instance you could insert n elements, get max, delete max etc and could sort numbers in O(n) time, while the theoretical lower bound is Omega(nlogn).

  • It's not lower bound O(n log n), there are circuits that can sort in O(n) and algorithms implementable in C that work in O(n log log n) – Daniel Aug 08 '10 at 20:50
  • Thoretical lower bound is O(n) (with exponential space) – Daniel Aug 08 '10 at 23:30
  • @Dani, and a non-deterministic machine? :) – Dimitris Andreou Aug 09 '10 at 00:18
  • @Dani: First off, use Omega for lower bounds. Second, if you use exponetial space, how can the time be linear? Even initializing the exponential space will be exponential. Sorry to say this, but you seem to be talking nonsense here. –  Aug 09 '10 at 17:21
  • Parallelization? The amount of steps that must be done in specific order is O(n), the rest can be parallel. – Daniel Aug 09 '10 at 22:52
0

Below program keeps track of max elements in stack in such a way that any point of time the top pointer would give us the max in the stack : So, max would be O(1), and we can find max by max[N]

ITEM   MAX

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

Java Program:

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());


}

}
0

Like some have already pointed out, the question lacks some information. You don't specify were to insert/delete, nor the nature of the data we are dealing with.

Some ideas that could be useful: You say,

insert/delete/maximum operation in O(1)

note that if we can insert, delete, and find maximun in O(1), then we can use this hipotetical technique to sort in O(n), because we can insert the n elements, and then take max/delete and we get them all sorted. It's proven that no sorting algorithm based in comparisons can sort in less than O(nlogn), so we know that no comparison based aproach will work. In fact, one of the fastest known ways of doing this is the Brodal queue, but it's deletion time exceeds O(1).

Maybe the solution is something like a radix tree, were the complexity of all these operations is related to the key length as oposed to the amount of keys. This is valid only if they let you bound the key length by some other number, so you can consider it constant.

But maybe it wasn't something that generic. Another interpretation, is that the insert/delete are the ones of a classic stack. In that restricted case, you can use the double stack solutiom that Can Berk Güder gave you.

Community
  • 1
  • 1
Martin Ventura
  • 167
  • 1
  • 3
  • 14
0

The best thing exists is: Insert in O(1) Delete in O(logn) Max/Min in O(1)

But to do that the insert function must create a link chain and you will also need an extra thread. The good news is that this link chain function also works in O(1) so it will not change the O(1) of insert.

Delete function doesnt break the link chain.

If the target of your delete is the max or min then the delete will be executed in O(1)

The data structure is a mix of an avl tree and a linked list.

The nature of a true delete is such that you cannot make it work in O(1). Hash tables which work with O(1) delete dont have the cabability to hold all the inputs.

-1

A hash table might support insert/delete in O(1), no clue about maximum though. You'd probably need to keep track of it yourself somehow.

VHristov
  • 1,059
  • 2
  • 13
  • 25