Table of contents
1.
Introduction 
2.
Problem Statement
3.
The Brute Force Approach
3.1.
Algorithm
3.2.
Implementation Of Brute Force Approach
3.3.
Complexity Analysis
4.
The Optimized Approach
4.1.
Why does this approach work?
5.
Implementation of Space Optimized Approach
5.1.
Complexity Analysis
6.
Frequently Asked Questions
6.1.
What is a stack?
6.2.
What is the minimum stack?
6.3.
How many minimum queues are needed to implement a stack?
6.4.
What is the time complexity of an operation in a minimum stack?
7.
Conclusion
Last Updated: Mar 27, 2024
Medium

Design a Minimum Stack

Author Kabir Singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?
Stacks

Introduction 

Stack is a linear data structure that allows the most recently added elements to be removed first and this feature is more popularly known as LIFO ( last in, first out). 

Stack problems are very common in technical interviews and if you are preparing to get into your dream company. You've landed in the right place, Champ!

Today's article will go over a stack problem called Design a stack that supports getMin() in O(1) time and O(1) extra space that is frequently asked in big product-based companies such as Amazon, Adobe, and Microsoft.

Also see, Data Structures

Problem Statement

Implement a SpecialStack Data Structure that supports getMin() in O(1) time and O(1) extra space along with push(), pop(), top(), isEmpty() in O(1) time. To implement SpecialStack, you should only use inbuilt Stack data structures and no other data structures like arrays, lists etc.

Before we get into the solutions, let's first understand the problem statement.

stack = SpecialStack() //Create an object of your special stack
stack.push(2)
stack.push(4)
stack.top() ----------> returns 4 as 4 is at the top(last inserted element)
stack.getMin() -------> returns 2 as it is the current minimum
stack.pop() 
stack.push(-1)
stack.getMin() -------> returns -1 as now it is the minimum element.

 

We can use various approaches to solve this problem. Let’s see what all approaches are possible for the solution of the minimum stack problem and how we can go through them.

Recommended: Please solve it on “Coding Ninjas Studio” first, before moving on to the solution.

The Brute Force Approach

The simplest solution that comes to mind is to use another stack to keep track of the minimum element so far. And adjust it for each push and pop operation.

Let's see how we can put it into action... 

Algorithm

  • To solve the problem, you must create two separate stacks let’s say (s1 and s2)
  • The first stack (s1) would contain the actual elements, and the second stack (s2) would contain the current stack's minimum element.
  • When we need to push an element into the stack, we must first determine whether or not the stack (s1) is empty. If the stack (s1) is empty, we just push the element into both stacks.
  • Otherwise, push the element in the stack (s1) and take the minimum of the top of the stack (s2) and the current element and push it in the stack s2.

Remember to keep the following factors in mind before calling the functionalities:

  • For POP(), first determine whether the stack (s1) is empty or not, if the stack is not empty save and pop the top of the stack (s1). Else return ‘-1.' Also remove the top element from the stack(s2) only if it is the minimum element
  • For TOP(), see if the stack (s1) is empty. If the stack is empty, return ‘-1'. Otherwise, return the top of the stack (s1).
  • For getMin(): Check if the stack is empty before calling getMin(). Return ‘-1' if the stack is empty. Otherwise, return the top of stack (s2).
  • If the stack is empty, ISEMPTY() returns True; otherwise, it returns False.

Implementation Of Brute Force Approach

#include <bits/stdc++.h>
using namespace std;
 
class MinStack
{
    //Primary stack to store elements
    stack<int> s1;       
    // temporary Stack to store minimum elements
    stack<int> s2;     
 
public:
    void push(int x)
    {   
        cout <<"Push element: "<< x << endl;
        s1.push(x);
        // if the tempStack is empty, push the given element into it
        if (s2.empty()) 
        {
            s2.push(x);
        }
        else 
        {
            // push the given element into the tempStack
            // if it is less than or equal to the current minimum
            if (s2.top() >= x) 
            {
                s2.push(x);
            }
        }
    }
 
    int pop()
    {
        if (empty())
        {
            cout << "Stack is Empty!!" << endl;
            return -1;
        }
 
        int top = s1.top();
        s1.pop();
 
        // remove the top element from the tempStack
        // only if it is the minimum element
        if (top == s2.top()) 
        {
            s2.pop();
        }
    
    return top;
    }
 
    int top() {
        return s1.top();
    }
 
    // Returns the size of the stack
    int size() {
        return s1.size();
    }
 
    bool empty() {
        return s1.empty();
    }
 
    // Function to return the minimum element from the stack in  //constant time
    int getMin()
    {
        if (s2.empty())
        {
            cout << "Stack is Empty!! ";
            return -1;
        }
 
        return s2.top();
    }
};
 
int main()
{
    MinStack s1;
    s1.push(6);
    s1.push(7);
    s1.push(8);
    cout <<"The minimum element is: "<< s1.getMin() << endl;
    s1.push(5);
    s1.push(3);
    cout <<"The minimum element is: "<< s1.getMin() << endl; 
    cout <<"Popped element: "<< s1.pop() << endl;
    s1.push(10);
    cout <<"The minimum element is: "<< s1.getMin() << endl;
    cout <<"Popped element: "<< s1.pop() << endl;
    cout <<"Popped element: "<< s1.pop() << endl;
    cout <<"The minimum element is: "<< s1.getMin() << endl;
 
    return 0;
}
You can also try this code with Online C++ Compiler
Run Code

 

Output-

Push element: 6

Push element: 7

Push element: 8

The minimum element is: 6

Push element: 5

Push element: 3

The minimum element is: 3

Popped element: 3

Push element: 10

The minimum element is: 5

Popped element: 10

Popped element: 5

The minimum element is: 6

Complexity Analysis

Time Complexity

The time complexity is as follows:

push(): O(1) - As insertion in a stack takes constant time.

pop(): O(1) - As deletion of the top element in a stack takes constant time.

top(): O(1) - Constant time is required.

getMin(): O(1) - As we have used an auxiliary stack that has it’s top as the minimum element.

isEmpty(): O(1) - Constant time is required.

Space Complexity

The space complexity is O(N) as auxiliary space is required to keep track of the minimum element.

Read More - Time Complexity of Sorting Algorithms

The Optimized Approach

Another way to solve this problem is by using the optimized approach by storing the smallest element of our stack as discovered in a special variable and returning the same at the end of the entire program of the minimum stack. But there’s a big elephant in the room! What if the minimum element is popped off? What do we do then?

Don’t worry, let's see the algorithm and then explanation why this approach actually works!!

Algorithm

Here, we will store the smallest element encountered at any point in time in a variable called ‘MinEle'

FOR PUSH (x) Operation where x is the element to be inserted, We have 3 cases:  
Case 1. IF THE STACK IS EMPTY
Simply insert the x into the stack and change the MinEle to x.

Case 2. IF STACK IS NOT EMPTY AND x>=STACK TOP VALUE
Simply insert the x into the stack.

Case 3. IF THE STACK IS NOT EMPTY AND x < STACK TOP VALUE
Push (2 * x - MinEle) into the stack and change the value of 'MinEle' to x.

For POP () operation
Case 1: Determine whether or not the stack is empty. We return ‘-1' if the stack is empty.


Case 2: If the stack’s top value >= MinEle
Simply remove the top value from the stack; the minimum value will remain unchanged.

Case 3: If the stack's top value < MinEle
Here top is the minimum element.
Thus to handle the case where the minimum element is removed, we would need to save the previous minimum element.
So, update the MinEle = (2 * MinEle - stack’s top value)

For TOP () operation
Case 1: Determine whether the stack is empty. If the stack is empty, Return -1.

Case 2: Determine whether the last inserted element of the stack is less than MinEle. If so, we will return MinEle. Otherwise, we will return the last element pushed into the stack.

For getMin() operation 
We will simply return the value of MinEle

Why does this approach work?

Taking the approach under consideration, we get two simple scenarios:

  1. (2 * x - MinEle ) < MinEle and,
  2. When the top element is minimum and it is popped, the previous minimum element was restored using (2 * MinEle  - stack’s top value)

Let’s Solve this using Simple Mathematics:

For Point 1:

x  < MinEle which proves that x - MinEle < 0 

//where x is the current element to be inserted.
//Adding x to both sides
We conclude that:
2 * x - MinEle < x

 

For Point 2:

//For simplicity let us take current element to be inserted as e, current minElement as m, previous minElement as p and n for new minElement


We know y was pushed as 2*e - p when e < current minElement
y = 2*e - p
// minElement was updated to e
m = e
n = 2*m - y
  = 2*e - (2*e - p)
  = p

 

So the new minElement is equal to the previous minElement. Hurrah! This is what we wanted.

Implementation of Space Optimized Approach

// C++ program to implement brute force approach
// getMin() in O(1) time and O(1) extra space.
#include <bits/stdc++.h>
using namespace std;

struct MyStack
{
    stack<int> stc;
    int MinEle;

    //printing the minimum stack value
    void getMin()
    {
        if (stc.empty())
            cout << "Stack is empty\n";

        // variable MinEl stores the minimum stack element
        // of the stack.
        else
            cout << "Minimum Element in the stack is: "
                << MinEle<< "\n";
    }

    //printing the top most element of the stack
    void peek()
    {
        if (stc.empty())
        {
            cout << "Stack is empty ";
            return;
        }

        int top = stc.top();

        cout << "Top Most Element is: ";

        (top < MinEle) ? cout << MinEle: cout << top;
    }


    void pop()
    {
        if (stc.empty())
        {
            cout << "Stack is empty\n";
            return;
        }

        cout << "Top Most Element Removed: ";
        int top = stc.top();
        stc.pop();

        if (top < MinEle)
        {
            cout << MinEle<< "\n";
            MinEle= 2 * MinEle- top;
        }

        else
            cout << top << "\n";
    }

 
    void push(int x)

    //here x is the element to be pushed
    {

        if (stc.empty())
        {
            MinEle= x;
            stc.push(x);
            cout << "Push Element: " << x << "\n";
            return;
        }

      
        if (x < MinEle)
        {
            stc.push(2 * x - MinEle);
            MinEle= x;
        }

        else
            stc.push(x);

        cout << "Push Element: " << x << "\n";
    }
};


int main()
{
    MyStack stc;
    stc.push(3);
    stc.push(5);
    stc.getMin();
    stc.push(2);
    stc.push(1);
    stc.getMin();
    stc.pop();
    stc.getMin();
    stc.pop();
    stc.getMin();

    return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Output-

Push Element: 3

Push Element: 5

Minimum Element in the stack is: 3

Push Element: 2

Push Element: 1

Minimum Element in the stack is: 1

Top Most Element Removed: 1

Minimum Element in the stack is: 2

Top Most Element Removed: 2

Minimum Element in the stack is: 3

Complexity Analysis

Time Complexity

push(): O(1) - Constant  time is required.

pop(): O(1) - Constant time is required.

top(): O(1) - Constant time is required.

getMin(): O(1) - Constant time is required as we are storing the min element in a special variable.

isEmpty(): O(1) - Constant time is required.

Space Complexity

O(1), No extra space is required.

If you've read this far, then without further ado, let’s try to submit the “Minimum Stack” problem on Coding Ninjas Studio and get it accepted right away.

Must Read Stack Operations

Frequently Asked Questions

What is a stack?

A stack is an abstract data structure that constitutes a collection of elements.It implements the “Last In First Out” mechanism.

What is the minimum stack?

A minimum stack can be defined as a stack that is eligible for performing 3 operations. The three operations are as follows:
Push() - To add an element to the stack.
Pop() - To remove an element from the stack.
min() - To return the current minimum stack value.

How many minimum queues are needed to implement a stack?

Two queues are required to implement the stack.

What is the time complexity of an operation in a minimum stack?

The time complexity of all the operations performed on a minimum stack is O(1).

Conclusion

In this blog, we learned about returning the minimum value in a Stack in O(1) time-

  • We started with the brute force approach, where we took two stacks, one storing the current number and the other storing the minimum stack value. 
  • The second approach was the space optimization approach where we used an extra variable to store the minimum element of the stack.

There are a lot of other problems related to stack which are frequently asked in interviews, some are:

Recommended Reading:

Do check out The Interview guide for Product Based Companies as well as some of the Popular Interview Problems from Top companies like Amazon, Adobe, Google, Uber, Microsoft, etc. on Coding Ninjas Studio.

You can also consider our Mern Stack Course to give your career an edge over others.

If you liked this blog, share it with your friends.

 

Live masterclass