36

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

9
  • 12
    Is this homework? Commented Aug 8, 2010 at 20:21
  • 11
    Insert at where? Delete from where? O(1) is amortized or exact? Commented Aug 8, 2010 at 20:22
  • 2
    I don't think this is homework. Commented Aug 8, 2010 at 20:24
  • 7
    I guess it is a silly interview question. :) Commented Aug 8, 2010 at 21:22
  • 1
    Side remark: Van Emde Boas trees 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). Commented Aug 8, 2010 at 23:34

8 Answers 8

57

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.

Sign up to request clarification or add additional context in comments.

15 Comments

-1: Same answer as Anurag's and does not actually answer the question (IMO).
i went to interview for last week some folks asked me this question, i suggested priority queue. your answer seems to be correct
@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.
its an acceptable answer for this question. but the question itself has no practical purpose other than confusing candidates
@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).
|
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.

7 Comments

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.
Thanks! I agree with what you said.
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
@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|.
@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)
|
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.

14 Comments

+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.
@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.
@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.
@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). :)
"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.
|
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).

5 Comments

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)
Thoretical lower bound is O(n) (with exponential space)
@Dani, and a non-deterministic machine? :)
@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.
Parallelization? The amount of steps that must be done in specific order is O(n), the rest can be parallel.
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()); } } 

Comments

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.

Comments

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.

Comments

-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.

5 Comments

Maximum is O(N) for a simple hash table.
It would be easy to amend a hashtable to keep track of the max, so I'm pretty sure this is along the right lines.
@Will: But that will make delete O(N).
@Will: Not really. How would you cater to deletes? What would you do if we happen to delete the maximum?
@KennyTM, @Moron very true - I engaged keyboard before brain again! :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.