views:

2372

answers:

6

This is one of an interview question. You need to design a stack which holds an integer value such that getMinimum() function should return the minimum element in the stack.

For example: consider the below example

case #1

5  --> TOP
1
4
6
2

When getMinimum() is called it should return 1, which is the minimum element 
in the stack. 

case #2

stack.pop()
stack.pop()

Note: Both 5 and 1 are poped out of the stack. So after this, the stack
looks like,

4  --> TOP
6
2

When getMinimum() is called is should return 2 which is the minimum in the 
stack.

Constriants:

  1. getMinimum should return the minimum value in O(1)
  2. Space constraint also has to be considered while designing it and if you use extra space, it should be of constant space.
+18  A: 

Add a field to hold the minimum value and update it during Pop() and Push(). That way getMinimum() will be O(1), but Pop() and Push() will have to do a little more work.

If minimum value is popped, Pop() will be O(n), otherwise they will still both be O(1). When resizing Push() becomes O(n) as per the Stack implementation.

Here's a quick implementation

public sealed class MinStack {
    private int MinimumValue;
    private readonly Stack<int> Stack = new Stack<int>();

    public int GetMinimum() {
        if (IsEmpty) {
            throw new InvalidOperationException("Stack is empty");
        }
        return MinimumValue;
    }

    public int Pop() {
        var value = Stack.Pop();
        if (value == MinimumValue) {
            MinimumValue = Stack.Min();
        }
        return value;
    }

    public void Push(int value) {
        if (IsEmpty || value < MinimumValue) {
            MinimumValue = value;
        }
        Stack.Push(value);
    }

    private bool IsEmpty { get { return Stack.Count() == 0; } }
}
Brian Rasmussen
sorry I didnt understand why does pop() and push() will suffer ?
Ganesh M
Because you have to check/update the minimum value when pushing/poping.
Brian Rasmussen
I agree, while pushing we need to check. But I feel we don't need to do anything extra while poping out the element ??
Ganesh M
If you pop the minimum value element, I assume you want to find the new minimum value.
Brian Rasmussen
In pop() the "new" minimum element has to be found, which takes O(n). Push() won't suffer, as this operation is still O(1).
Georg
@Brian Rasmussen, the new minimum value will be stored in the elements addition field while we do the push !! so we dont need to find it again after the pop. If this is not clear I will post the solution.
Ganesh M
@gs: good point, it is a bit more work, but it is still O(1).
Brian Rasmussen
pop() suffers only if it removes the minimum value.
sigjuice
@sigjuice: correct. I think I will change the word "suffer" to something less dramatic :)
Brian Rasmussen
@Ganesh: If you're adding a field to the *element* type (rather than the stack type), that's equivalent to having two stacks, my original answer. But we can do better - see my edit.
Jon Skeet
@Ganesh M "the elements addition field" if you have an additional field in your N elements, it's not constant space, but O(N) extra.
Pete Kirkham
+1 Given the rules, this one is the answer.
eglasius
+14  A: 

EDIT: This fails the "constant space" constraint - it basically doubles the space required. I very much doubt that there's a solution which doesn't do that though, without wrecking the runtime complexity somewhere (e.g. making push/pop O(n)). Note that this doesn't change the complexity of the space required, e.g. if you've got a stack with O(n) space requirements, this will still be O(n) just with a different constant factor.

Non-constant-space solution

Keep a "duplicate" stack of "minimum of all values lower in the stack". When you pop the main stack, pop the min stack too. When you push the main stack, push either the new element or the current min, whichever is lower. getMinimum() is then implemented as just minStack.peek().

So using your example, we'd have:

Real stack        Min stack

5  --> TOP        1
1                 1
4                 2
6                 2
2                 2

After popping twice you get:

Real stack        Min stack

4                 2
6                 2
2                 2

Please let me know if this isn't enough information. It's simple when you grok it, but it might take a bit of head-scratching at first :)

(The downside of course is that it doubles the space requirement. Execution time doesn't suffer significantly though - i.e. it's still the same complexity.)

EDIT: There's a variation which is slightly more fiddly, but has better space in general. We still have the min stack, but we only pop from it when the value we pop from the main stack is equal to the one on the min stack. We only push to the min stack when the value being pushed onto the main stack is less than or equal to the current min value. This allows duplicate min values. getMinimum() is still just a peek operation. For example, taking the original version and pushing 1 again, we'd get:

Real stack        Min stack

1  --> TOP        1
5                 1
1                 2
4                 
6                 
2

Popping from the above pops from both stacks because 1 == 1, leaving:

Real stack        Min stack

5  --> TOP        1
1                 2
4                 
6                 
2

Popping again only pops from the main stack, because 5 > 1:

Real stack        Min stack

1                 1
4                 2
6                 
2

Popping again pops both stacks because 1 == 1:

Real stack        Min stack

4                 2
6                 
2

This ends up with the same worst case space complexity (double the original stack) but much better space usage if we rarely get a "new minimum or equal".

EDIT: Here's an implementation of Pete's evil scheme. I haven't tested it thoroughly, but I think it's okay :)

using System.Collections.Generic;

public class FastMinStack<T>
{
    private readonly Stack<T> stack = new Stack<T>();
    // Could pass this in to the constructor
    private readonly IComparer<T> comparer = Comparer<T>.Default;

    private T currentMin;

    public T Minimum
    {
        get { return currentMin; }
    }

    public void Push(T element)
    {
        if (stack.Count == 0 ||
            comparer.Compare(element, currentMin) <= 0)
        {
            stack.Push(currentMin);
            stack.Push(element);
            currentMin = element;
        }
        else
        {
            stack.Push(element);
        }
    }

    public T Pop()
    {
        T ret = stack.Pop();
        if (comparer.Compare(ret, currentMin) == 0)
        {
            currentMin = stack.Pop();
        }
        return ret;
    }
}
Jon Skeet
That's hardly constant space, though.
Konrad Rudolph
and i worried about the runtime also ..
Ganesh M
@Konrad: I hadn't seen the space constraint. Will leave answer, but edit to explain the problem. @Ganesh: What worries you about that? If you've already got an efficient stack (e.g. using a linked list) then it's just double that, which is the same complexity.
Jon Skeet
Clever! @Ganesh: Why would the runtime be a problem? It will take only twice as long as a single stack, i.e. it's still O(1) time for push() and pop() as well as for getMinimum() -- that's *excellent* performance!
j_random_hacker
@Jon Skeet, I agree, the run time is O(1). But instead of having a duplicate stack, how about having a integer variable called currentMinimum in the stack design itself ? this eliminates poping out the duplicate stack whenever there is a pop in the original ?
Ganesh M
If you have a single variable, what happens when you pop the "1" in your example? It's got to work out that the previous minimum was "2" - which it can't without scanning through everything.
Jon Skeet
@Ganesh: Won't you then need to find the new minimum using an O(n) search whenever you pop()?
j_random_hacker
If the space issue really is a problem then rather than storing a second stack you could store a stack of minimums and the number of times that they occur. On each pop you decrease the counter at the top, when it hits 0 you pop the actual value. The min stack in the example becomes: [(1,2), (2,3)]
Jack Ryan
@j_random_hacke, No we dont. We store the current mimimum while pushing the data.
Ganesh M
@JayArr: Nice variation, although it makes it even worse in the case of a decreasing stack :) We can do slightly better though - I'll edit.
Jon Skeet
Ganesh: So when you pop "1" in your original example, how do you come up with "2"? Btw, anyone following this, you might want to check out my edit for a more space-efficient (but fiddly) form for most situations.
Jon Skeet
Just reading your other comments, when you say "in the stack design itself" do you mean "in each element"? If so, you're still potentially nearly doubling the memory requirements, depending on the size of the element type. It's conceptually the same as two stacks.
Jon Skeet
@Jon Skeet, thats true. Add in each element. Agreed as per the space, its same as having one more stack. but it eliminates poping from the second stack. I will post my solution in a while. but anyhow, your solution is also good .. +1
Ganesh M
@Ganesh: Unfortunately having no extra stack means we can't make the space-saving optimisation I've included above. Keeping "minimum and element" together is probably more efficient than two stacks of the same size (fewer overheads - arrays, list nodes etc) although it will depend on language.
Jon Skeet
(In particular, in Java you'd need a new object to hold the two values - the overhead of the object could be significant. In .NET you could just use a struct with them both in, which is nice and efficient.)
Jon Skeet
Just do two pops off the one stack if the top equal to the min value, the second pop giving the new min.
Pete Kirkham
@Pete: So a single stack contains both values? And you have a separate single variable holding "current minimum"? Hmm... interesting idea.
Jon Skeet
Having thought about Pete's version, I'd quite enjoy implementing it, if anyone is actually interested in seeing what it would look like in C#.
Jon Skeet
+4  A: 

Well, what are the runtime constraints of push and pop? If they are not required to be constant, then just calculate the minimum value in those two operations (making them O(n)). Otherwise, I don't see how this can be done with constant additional space.

Konrad Rudolph
+1, hehe... The old "bend the rules" trick... In a similar way, I know of a sorting algorithm that sorts any size array in O(1) time, but the first access to any element of the result incurs O(nlog n) overhead... :)
j_random_hacker
In Haskell, everything is constant time! (except if you want to print the result)
Henk
A: 
Ganesh M
I Agree, this requires an additional element in your struct. But this eliminates finding the minimum whenever we pop an element.
Ganesh M
So, having failed to meet the constraints of the question, did you get the job?
Pete Kirkham
nope I didn't :)
Ganesh M
+1  A: 
public class StackWithMin {
    int min;
    int size;
    int[] data = new int[1024];

    public void push ( int val ) {
        if ( size == 0 ) {
            data[size] = val;
            min = val;
        } else if ( val < min) {
            data[size] = 2 * val - min;
            min = val;

            assert (data[size] < min); 
        } else {
            data[size] = val;
        }

        ++size;

        // check size and grow array
    }

    public int getMin () {
        return min;
    }

    public int pop () {
        --size;

        int val = data[size];

        if ( ( size > 0 ) && ( val < min ) ) {
            int prevMin = min;
            min += min - val;
            return prevMin;
        } else {
            return val;
        }
    }

    public boolean isEmpty () {
        return size == 0;
    }

    public static void main (String...args) {
        StackWithMin stack = new StackWithMin();

        for ( String arg: args ) 
            stack.push( Integer.parseInt( arg ) );

        while ( ! stack.isEmpty() ) {
            int min = stack.getMin();
            int val = stack.pop();

            System.out.println( val + " " + min );
        }

        System.out.println();
    }

}

It stores the current minimum explicitly, and if the minimum changes, instead of pushing the value, it pushes a value the same difference the other side of the new minimum ( if min = 7 and you push 5, it pushes 3 instead ( 5-|7-5| = 3) and sets min to 5; if you then pop 3 when min is 5 it sees that the popped value is less than min, so reverses the procedure to get 7 for the new min, then returns the previous min). As any value which doesn't cause a change the current minimum is greater than the current minimum, you have something that can be used to differentiate between values which change the minimum and ones which don't.

In languages which use fixed size integers, you're borrowing a bit of space from the representation of the values, so it may underflow and the assert will fail. But otherwise, it's constant extra space and all operations are still O(1).

Stacks which are based instead on linked lists have other places you can borrow a bit from, for example in C the least significant bit of the next pointer, or in Java the type of the objects in the linked list. For Java this does mean there's more space used compared to a contiguous stack, as you have the object overhead per link:

public class LinkedStackWithMin {
    private static class Link {
        final int value;
        final Link next;

        Link ( int value, Link next ) {
            this.value = value;
            this.next = next;
        }

        int pop ( LinkedStackWithMin stack ) {
            stack.top = next;
            return value;
        }
    }

    private static class MinLink extends Link {
        MinLink ( int value, Link next ) {
            super( value, next );
        }

        int pop ( LinkedStackWithMin stack ) {
            stack.top = next;
            int prevMin = stack.min;
            stack.min = value;
            return prevMin;
        }
    }

    Link top;
    int min;

    public LinkedStackWithMin () {
    }

    public void push ( int val ) {
        if ( ( top == null ) || ( val < min ) ) {
            top = new MinLink(min, top);
            min = val;
        } else {
            top = new Link(val, top);
        }
    }

    public int pop () {
        return top.pop(this);
    }

    public int getMin () {
        return min;
    }

    public boolean isEmpty () {
        return top == null;
    }

In C, the overhead isn't there, and you can borrow the lsb of the next pointer:

typedef struct _stack_link stack_with_min;

typedef struct _stack_link stack_link;

struct _stack_link {
    size_t  next;
    int     value;
};

stack_link* get_next ( stack_link* link ) 
{
    return ( stack_link * )( link -> next & ~ ( size_t ) 1 );
}

bool is_min ( stack_link* link )
{
    return ( link -> next & 1 ) ! = 0;
}

void push ( stack_with_min* stack, int value )
{
    stack_link *link = malloc ( sizeof( stack_link ) );

    link -> next = ( size_t ) stack -> next;

    if ( (stack -> next == 0) || ( value == stack -> value ) ) {
        link -> value = stack -> value;
        link -> next |= 1; // mark as min
    } else {
        link -> value = value;
    }

    stack -> next = link;
}

etc.;

However, none of these are truly O(1). They don't require any more space in practice, because they exploit holes in the representations of numbers, objects or pointers in these languages. But a theoretical machine which used a more compact representation would require an extra bit to be added to that representation in each case.

Pete Kirkham
A: 

Nice implementation:

http://www.rawkam.com/?p=1337

Sunil Singh