Open In App

Introduction to Monotonic Stack – Data Structure and Algorithm Tutorials

Last Updated : 31 May, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

A monotonic stack is a special data structure used in algorithmic problem-solving. Monotonic Stack maintaining elements in either increasing or decreasing order. It is commonly used to efficiently solve problems such as finding the next greater or smaller element in an array etc.

Monotonic-Stack-banner

Monotonic Stack

What is Monotonic Stack?

A Monotonic Stack is a common data structure in computer science that maintains its elements in a specific order. Unlike traditional stacks, Monotonic Stacks ensure that elements inside the stack are arranged in an increasing or decreasing order based on their arrival time. In order to achieve the monotonic stacks, we have to enforce the push and pop operation depending on whether we want a monotonic increasing stack or monotonic decreasing stack.

Let’s understand the term Monotonic Stacks by breaking it down.

Monotonic: It is a word for mathematics functions. A function y = f(x) is monotonically increasing or decreasing when it follows the below conditions: 

  • As x increases, y also increases always, then it’s a monotonically increasing function. 
  • As x increases, y decreases always, then it’s a monotonically decreasing function.

See the below examples:

  • y = 2x +5, it’s a monotonically increasing function.
  • y = -(2x), it’s a monotonically decreasing function.  

Similarly, A stack is called a monotonic stack if all the elements starting from the bottom of the stack is either in increasing or in decreasing order.

Types of Monotonic Stack:

Monotonic Stacks can be broadly classified into two types:

  1. Monotonic Increasing Stack
  2. Monotonic Decreasing Stack

Monotonic Increasing Stack:

A Monotonically Increasing Stack is a stack where elements are placed in increasing order from the bottom to the top. Each new element added to the stack is greater than or equal to the one below it. If a new element is smaller, we remove elements from the top of the stack until we find one that is smaller or equal to the new element, or until the stack is empty. This ensures that the stack always stays in increasing order.

Example: 1, 3, 10, 15, 17

How to achieve Monotonic Increasing Stack?

To achieve a monotonic increasing stack, you would typically push elements onto the stack while ensuring that the stack maintains a increasing order from bottom to top. When pushing a new element, you would pop elements from the stack that are smaller than the new element until the stack maintains the desired monotonic increasing property.

To achieve a monotonic increasing stack, you can follow these step-by-step approaches:

  • Initialize an empty stack.
  • Iterate through the elements and for each element:
    • while stack is not empty AND top of stack is more than the current element
      • pop element from the stack
    • Push the current element onto the stack.
  • At the end of the iteration, the stack will contain the monotonic increasing order of elements.

Let’s illustrate the example for a monotonic increasing stack using the array Arr[] = {1, 7, 9, 5}:

Below is the implementation of the above approach:

C++
#include <iostream>
#include <stack>
#include <vector>

using namespace std;

// Function to implement monotonic increasing stack
vector<int> monotonicIncreasing(vector<int>& nums)
{
    int n = nums.size();
    stack<int> st;
    vector<int> result;

    // Traverse the array
    for (int i = 0; i < n; ++i) {

        // While stack is not empty AND top of stack is more
        // than the current element
        while (!st.empty() && st.top() > nums[i]) {

            // Pop the top element from the
            // stack
            st.pop();
        }

        // Push the current element into the stack
        st.push(nums[i]);
    }

    // Construct the result array from the stack
    while (!st.empty()) {
        result.insert(result.begin(), st.top());
        st.pop();
    }

    return result;
}

int main() {
    // Example usage:
    vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
    vector<int> result = monotonicIncreasing(nums);
    cout << "Monotonic increasing stack: ";
    for (int num : result) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}
Java
import java.util.ArrayDeque;
import java.util.Deque;

public class MonotonicIncreasingStack {
    // Function to implement monotonic increasing stack
    public static int[] monotonicIncreasing(int[] nums) {
        Deque<Integer> stack = new ArrayDeque<>();
        
        // Traverse the array
        for (int num : nums) {
            // While stack is not empty AND top of stack is more than the current element
            while (!stack.isEmpty() && stack.peekLast() > num) {
                // Pop the top element from the stack
                stack.pollLast();
            }
            // Push the current element into the stack
            stack.offerLast(num);
        }

        // Construct the result array from the stack
        int[] result = new int[stack.size()];
        int index = stack.size() - 1;
        while (!stack.isEmpty()) {
            result[index--] = stack.pollLast();
        }

        return result;
    }

    // Main method for example usage
    public static void main(String[] args) {
        // Example usage:
        int[] nums = {3, 1, 4, 1, 5, 9, 2, 6};
        int[] result = monotonicIncreasing(nums);
        System.out.print("Monotonic increasing stack: [");
        for (int i = 0; i < result.length; i++) {
            System.out.print(result[i]);
            if (i != result.length - 1) {
                System.out.print(", ");
            }
        }
        System.out.println("]");
    }
}
Python
def monotonicIncreasing(nums):
    stack = []
    result = []

    # Traverse the array
    for num in nums:
        # While stack is not empty AND top of stack is more than the current element
        while stack and stack[-1] > num:
            # Pop the top element from the stack
            stack.pop()
        # Push the current element into the stack
        stack.append(num)

    # Construct the result array from the stack
    while stack:
        result.insert(0, stack.pop())

    return result

# Example usage:
nums = [3, 1, 4, 1, 5, 9, 2, 6]
result = monotonicIncreasing(nums)
print("Monotonic increasing stack:", result)
JavaScript
// Function to implement monotonic increasing stack
function monotonicIncreasing(nums) {
    const stack = [];
    const result = [];

    // Traverse the array
    for (let i = 0; i < nums.length; ++i) {
        // While stack is not empty AND top of stack is more than the current element
        while (stack.length > 0 && stack[stack.length - 1] > nums[i]) {
            // Pop the top element from the stack
            stack.pop();
        }
        // Push the current element into the stack
        stack.push(nums[i]);
    }

    // Construct the result array from the stack
    while (stack.length > 0) {
        result.unshift(stack.pop());
    }

    return result;
}

// Example usage:
const nums = [3, 1, 4, 1, 5, 9, 2, 6];
const result = monotonicIncreasing(nums);
console.log("Monotonic increasing stack:", result);

Output
Monotonic increasing stack: 1 1 2 6 

Complexity Analysis:

  • Time Complexity: O(N), each element from the input array is pushed and popped from the stack exactly once. Therefore, even though there is a loop inside a loop, no element is processed more than twice.
  • Auxiliary Space: O(N)

Monotonic Decreasing Stack:

A Monotonically Decreasing Stack is a stack where elements are placed in decreasing order from the bottom to the top. Each new element added to the stack must be smaller than or equal to the one below it. If a new element is greater than top of stack then we remove elements from the top of the stack until we find one that is greater or equal to the new element, or until the stack is empty. This ensures that the stack always stays in decreasing order.

Example: 17, 14, 10, 5, 1

How to achieve Monotonic Decreasing Stack?

To achieve a monotonic decreasing stack, you would typically push elements onto the stack while ensuring that the stack maintains a decreasing order from bottom to top. When pushing a new element, you would pop elements from the stack that are greater than the new element until the stack maintains the desired monotonic decreasing property.

To achieve a monotonic decreasing stack, you can follow these step-by-step approaches:

  • Start with an empty stack.
  • Iterate through the elements of the input array.
    • While stack is not empty AND top of stack is less than the current element:
      • pop element from the stack
    • Push the current element onto the stack.
  • After iterating through all the elements, the stack will contain the elements in monotonic decreasing order.

Let’s illustrate the example for a monotonic decreasing stack using the array Arr[] = {7, 5, 9, 4}:

Below is the implementation of the above approach:

C++
#include <iostream>
#include <stack>
#include <vector>

using namespace std;

// Function to implement monotonic decreasing stack
vector<int> monotonicDecreasing(vector<int>& nums) {
    int n = nums.size();
    stack<int> st;
    vector<int> result(n);

    // Traverse the array
    for (int i = 0; i < n; ++i) {
        // While stack is not empty AND top of stack is less than the current element
        while (!st.empty() && st.top() < nums[i]) {
            st.pop();
        }

        // Construct the result array
        if (!st.empty()) {
            result[i] = st.top();
        } else {
            result[i] = -1;
        }

        // Push the current element into the stack
        st.push(nums[i]);
    }

    return result;
}

int main() {
    vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
    vector<int> result = monotonicDecreasing(nums);

    cout << "Monotonic decreasing stack: ";
    for (int val : result) {
        cout << val << " ";
    }
    cout << endl;

    return 0;
}
Java
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class MonotonicDecreasing {
    public static List<Integer> monotonicDecreasing(int[] nums) {
        Stack<Integer> stack = new Stack<>();
        List<Integer> result = new ArrayList<>();

        // Traverse the array
        for (int num : nums) {
            // While stack is not empty AND top of stack is less than the current element
            while (!stack.isEmpty() && stack.peek() < num) {
                // Pop the top element from the stack
                stack.pop();
            }

            // Construct the result array
            if (!stack.isEmpty()) {
                result.add(stack.peek());
            } else {
                result.add(-1);
            }

            // Push the current element into the stack
            stack.push(num);
        }

        return result;
    }

    public static void main(String[] args) {
        int[] nums = {3, 1, 4, 1, 5, 9, 2, 6};
        List<Integer> result = monotonicDecreasing(nums);
        System.out.println("Monotonic decreasing stack: " + result);
    }
}
Python
def monotonicDecreasing(nums):
    stack = []
    result = []

    # Traverse the array
    for num in nums:
        # While stack is not empty AND top of stack is less than the current element
        while stack and stack[-1] < num:
            # Pop the top element from the stack
            stack.pop()
        
        # Construct the result array
        if stack:
            result.append(stack[-1])
        else:
            result.append(-1)
        
        # Push the current element into the stack
        stack.append(num)

    return result

# Example usage:
nums = [3, 1, 4, 1, 5, 9, 2, 6]
result = monotonicDecreasing(nums)
print("Monotonic decreasing stack:", result)
JavaScript
function monotonicDecreasing(nums) {
    let stack = [];
    let result = [];

    // Traverse the array
    for (let num of nums) {
        // While stack is not empty AND top of stack is less than the current element
        while (stack.length && stack[stack.length - 1] < num) {
            // Pop the top element from the stack
            stack.pop();
        }

        // Construct the result array
        if (stack.length) {
            result.push(stack[stack.length - 1]);
        } else {
            result.push(-1);
        }

        // Push the current element into the stack
        stack.push(num);
    }

    return result;
}

// Example usage:
let nums = [3, 1, 4, 1, 5, 9, 2, 6];
let result = monotonicDecreasing(nums);
console.log("Monotonic decreasing stack:", result);

Output
Monotonic decreasing stack: -1 3 -1 4 -1 -1 9 9 

Complexity Analysis:

  • Time Complexity: O(N), each element is processed only twice, once for the push operation and once for the pop operation.
  • Auxiliary Space: O(N) 

Practice Problem on Monotonic Stack:

Problem

Next Greater Element (NGE) for every element in given Array

Next Smaller Element

Find next Smaller of next Greater in an array

Next Greater Frequency Element

Largest Rectangular Area in a Histogram using Stack

Check for Balanced Brackets in an expression (well-formedness)

Maximum size rectangle binary sub-matrix with all 1s

Expression Evaluation

The Stock Span Problem

Expression contains redundant bracket or not

Find the nearest smaller numbers on left side in an array

Find maximum of minimum for every window size in a given array

Minimum number of bracket reversals needed to make an expression balanced

Tracking current Maximum Element in a Stack

Lexicographically largest subsequence containing all distinct characters only once

Sum of maximum elements of all possible sub-arrays of an array

Applications of Monotonic Stack :

Here are some applications of monotonic stacks:

  • Finding Next Greater Element: Monotonic stacks are often used to find the next greater element for each element in an array. This is a common problem in competitive programming and has applications in various algorithms.
  • Finding Previous Greater Element: Similarly, monotonic stacks can be used to find the previous greater element for each element in an array.
  • Finding Maximum Area Histogram: Monotonic stacks can be applied to find the maximum area of a histogram. This problem involves finding the largest rectangular area possible in a given histogram.
  • Finding Maximum Area in Binary Matrix: Monotonic stacks can also be used to find the maximum area of a rectangle in a binary matrix. This is a variation of the maximum area histogram problem.
  • Finding Sliding Window Maximum/Minimum: Monotonic stacks can be used to efficiently find the maximum or minimum elements within a sliding window of a given array.
  • Expression Evaluation: Monotonic stacks can be used to evaluate expressions involving parentheses, such as checking for balanced parentheses or evaluating the value of an arithmetic expression.

Advantages of Monotonic Stack:

  • Efficient for finding the next greater or smaller element in an array.
  • Useful for solving a variety of problems, such as finding the nearest smaller element or calculating the maximum area of histograms.
  • In many cases, the time complexity of algorithms using monotonic stacks is linear, making them efficient for processing large datasets.

Disadvantages of Monotonic Stack:

  • Requires extra space to store the stack.
  • May not be intuitive for beginners to understand and implement.

Frequently Asked Questions (FAQs) on Monotonic Stack:

1. What is a monotonic stack?

A monotonic stack is a data structure that maintains either non-increasing or non-decreasing order of elements.

2. How is a monotonic stack different from a regular stack?

Unlike a regular stack, a monotonic stack ensures that the elements are in a specific order, either non-increasing or non-decreasing.

3. What are the typical use cases for a monotonic stack?

Monotonic stacks are commonly used in problems involving finding the next greater element, next smaller element, or solving various algorithmic problems efficiently.

4. Can you provide an example of solving a problem using a monotonic stack?

One common example is finding the next greater element to the right for each element in an array using a monotonic stack.

5. Are there any common pitfalls or challenges when using a monotonic stack?

A common challenge is understanding when to use a monotonic stack and ensuring that the stack’s ordering is maintained correctly throughout the algorithm.

Related Article:



Previous Article
Next Article

Similar Reads

How to Identify and Solve Monotonic Stack Problems ?
We all know what is Stack and how it works so today we will learn about a special type of data structure called monotonic stack. Problems using monotonic stack are difficult to identify if you do not know its concept. So in this post, we are going to discuss some key points that will help us to identify these problems. But Before that let's discuss
10 min read
Introduction to Monotonic Queues
A monotonic queue is a data structure that supports efficient insertion, deletion, and retrieval of elements in a specific order, typically in increasing or decreasing order. The monotonic queue can be implemented using different data structures, such as a linked list, stack, or deque. The most common implementation is using a deque (double-ended q
8 min read
Find element position in given monotonic sequence
Given an integer k and a monotonic increasing sequence: f(n) = an + bn [log2(n)] + cn^3 where (a = 1, 2, 3, ...), (b = 1, 2, 3, ...), (c = 0, 1, 2, 3, ...) Here, [log2(n)] means, taking the log to the base 2 and round the value down Thus, if n = 1, the value is 0. if n = 2-3, the value is 1. if n = 4-7, the value is 2. if n = 8-15, the value is 3.
9 min read
Check if given Array is Monotonic
Given an array arr[] containing N integers, the task is to check whether the array is monotonic or not (monotonic means either the array is in increasing order or in decreasing order). Examples: Input: arr[] = {1, 2, 2, 3}Output: YesExplanation: Here 1 &lt; 2 &lt;= 2 &lt; 3. The array is in increasing order. Therefore it is monotonic. Input: arr[]
10 min read
Check if level-wise Decimal equivalent of Binary Tree forms a Monotonic sequence or not
Given the root of a binary tree in which all nodes has values either 0 or 1, the task is to check if the level-wise decimal equivalent of given Tree forms a monotonic sequence.or not. A sequence is monotonic if it is either monotone increasing or monotone decreasing. A sequence nums is monotone increasing if for all i &lt;= j, nums[i] &lt;= nums[j]
15+ min read
Monotonic shortest path from source to destination in Directed Weighted Graph
Given a weighted directed graph with N vertices and M edges, a source src and a destination target, the task is to find the shortest monotonic path (monotonically increasing or decreasing) from the source to the destination. Output -1 if no monotonic path is possible. Note: All weights are non-negative Examples: Input: N = 6, M = 9, src = 1, target
14 min read
Python Program to check if given array is Monotonic
Given an array A containing n integers. The task is to check whether the array is Monotonic or not. An array is monotonic if it is either monotone increasing or monotone decreasing. An array A is monotone increasing if for all i &lt;= j, A[i] &lt;= A[j]. An array A is monotone decreasing if for all i &lt;= j, A[i] &gt;= A[j]. Return Type: Boolean v
7 min read
Static Data Structure vs Dynamic Data Structure
Data structure is a way of storing and organizing data efficiently such that the required operations on them can be performed be efficient with respect to time as well as memory. Simply, Data Structure are used to reduce complexity (mostly the time complexity) of the code. Data structures can be two types : 1. Static Data Structure 2. Dynamic Data
4 min read
Design and Implement Special Stack Data Structure | Added Space Optimized Version
Question: Design a Data Structure SpecialStack that supports all the stack operations like push(), pop(), isEmpty(), isFull() and an additional operation getMin() which should return minimum element from the SpecialStack. All these operations of SpecialStack must be O(1). To implement SpecialStack, you should only use standard Stack data structure
15+ min read
Stack Vs Heap Data Structure
What is Stack? A stack is a linear data structure where the last element entered exits first. The order of stack data structure might be LIFO, FILO: According to this technique, the piece that is in last will come out first. As an example, consider a stack of dishes stacked on top of each other. The plate we put last is on top, and because we take
3 min read
Practice Tags :
three90RightbarBannerImg