Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intuition behind using a monotonic stack

I am solving a question on LeetCode.com:

Given an array of integers A, find the sum of min(B), where B ranges over every (contiguous) subarray of A. Since the answer may be large, return the answer modulo 10^9 + 7.

Input: [3,1,2,4]
Output: 17
Explanation: Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4]. Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1. Sum is 17.

A highly upvoted solution is as below:

class Solution {
public:
  int sumSubarrayMins(vector<int>& A) {
    stack<pair<int, int>> in_stk_p, in_stk_n;
    // left is for the distance to previous less element
    // right is for the distance to next less element
    vector<int> left(A.size()), right(A.size());

    //initialize
    for(int i = 0; i < A.size(); i++) left[i] =  i + 1;
    for(int i = 0; i < A.size(); i++) right[i] = A.size() - i;

    for(int i = 0; i < A.size(); i++){
      // for previous less
      while(!in_stk_p.empty() && in_stk_p.top().first > A[i]) in_stk_p.pop();
      left[i] = in_stk_p.empty()? i + 1: i - in_stk_p.top().second;
      in_stk_p.push({A[i],i});

      // for next less
      while(!in_stk_n.empty() && in_stk_n.top().first > A[i]){
        auto x = in_stk_n.top();in_stk_n.pop();
        right[x.second] = i - x.second;
      }
      in_stk_n.push({A[i], i});
    }

    int ans = 0, mod = 1e9 +7;
    for(int i = 0; i < A.size(); i++){
      ans = (ans + A[i]*left[i]*right[i])%mod;
    }
    return ans;
  }
};

My question is: what is the intuition behind using a monotonically increasing stack for this? How does it help calculate the minimums in the various subarrays?

like image 553
J. Doe Avatar asked Apr 21 '19 05:04

J. Doe


People also ask

How do you know when to use a monotonic stack?

Monotonic stacks are often used to track the maximum or minimum element in a data set. For example, let's say you have an array of integers and you want to keep track of the largest integer in the array at each index. You could use a monotonic stack for this.

Why does monotonic stack work?

Because it only updates information within the range based on new adding elements and avoids repetitive operations of existing elements. To be more precisely, the monotonic stack helps us maintain maximum and and minimum elements in the range and keeps the order of elements in the range.

What is monotonic stack in Java?

Monotonic stack is a stack that from top to bottom, elements are monotonically increasing. From right to left, we can check whether the top of the stack is bigger than the one in array. If it is not bigger than the one in array, it cannot be the solution, since the one in array is farther than it.

What is a monotonic problem?

A real function or sequence is called monotonic if it either constantly increases or decreases. Thus, the sequence of powers of 2 is monotonically increasing because each term is larger than the previous. The function is monotonically decreasing on the interval and monotonically increasing on the interval .


1 Answers

Visualize the array as a line graph, with (local) minima as valleys. Each value is relevant for a range that extends from just after the previous smaller value (if any) to just before the next smaller value (if any). (Even a value larger than its neighbors matters when considering the singleton subarray that contains it.) The variables left and right track that range.

Recognizing that a value shadows every value larger than it in each direction separately, the stack maintains a list of previous, unshadowed minima for two purposes: identifying how far back a new small number’s range extends and (at the same time) how far forward the invalidated minima’s ranges extend. The code uses a separate stack for each purpose, but there’s no need: each has the same contents after every iteration of the (outer) loop.

like image 119
Davis Herring Avatar answered Sep 18 '22 17:09

Davis Herring