The Thought and Application of Monotone Stack
Recently, I have seen the use of monotonic stack in several different situations. This time, I will systematically summarize the idea of monotonic stack and its use in various situations.
Stack is a very simple data structure, with a logical order of first in and last out, which meets the characteristics of certain problems, such as function call stack.
A monotonic stack is actually a stack, but with some clever logic, every time a new element is added to the stack, the elements in the stack remain in order (monotonically increasing or monotonically decreasing).
The monotonic stack is not very versatile and only deals with a typical problem called the Next Greater (Or Smaller) Element.
The use of monotone stack is also divided into situations:
Divided into two categories from finding the nearest larger or smaller element.
There are three cases from whether to find the next nearest larger (or smaller) element from the left, right, or both sides.
So there are 6 cases in total.
Right
The nearest larger element on the right
Let’s first look at the simplest case, from finding the nearest element to the right of the element that is larger than it, this example on leetcode is called: https://leetcode-cn.com/problems/next-greater-element-i
For example, if you enter an array nums = [2, 1, 2, 4, 3], you return the array [4, 2, 4, -1, -1].
Explanation: The number larger than 2 after the first 2 is 4; the number larger than 1 after 1 is 2; the number larger than 2 after the second 2 is 4; there is no number larger than 4 after 4, fill in -1; There is no number larger than 3 after 3, fill in -1.
The brute force solution to this problem is easy to think of, that is, scan behind each element to find the first larger element. But the time complexity of the brute force solution is’ O (n ^ 2) '.
This question can be thought of abstractly like this: imagine the elements of an array as people standing side by side, and the size of the elements as the height of an adult. These people stand in a column facing you, how to find the Next Greater Number of element “2”? Very simple, if you can see element “2”, then the first person visible behind him is the Next Greater Number of “2”, because the element smaller than “2” is not tall enough, it is blocked by “2”, and the first one exposed is the answer.
1 | vector<int> nextGreaterElement(vector<int>& nums) { |
This is the template for solving the problem of monotonic queues. The rest of the cases are modified from this template. The for loop scans elements from back to front, because we use the stack structure to put them onto the stack backwards, which is actually going out of the stack backwards. The while loop excludes the elements between two “taller” elements, because their existence is meaningless and there is a “taller” element in front, so they cannot be used as the Next Great Number of incoming elements
The nearest smaller element on the right
Compared with the previous case, this case is that the judgment condition of the while loop is modified from less than or equal to greater than or equal to.
1 | vector<int> nextGreaterElement(vector<int>& nums) { |
Left
The nearest larger element on the left
The for loop starts on the left side compared to the right
1 | vector<int> nextGreaterElement(vector<int>& nums) { |
The nearest smaller element on the left
Compared with the previous case, this case is that the judgment condition of the while loop is modified from less than or equal to greater than or equal to.
1 | vector<int> nextGreaterElement(vector<int>& nums) { |
Both sides
You can see this example: https://leetcode-cn.com/problems/next-greater-element-ii/
The same is Next Greater Number. Now suppose the array given to you is a ring. How to deal with it? The 503 question of the force buckle “Next Greater Element II” is this question:
For example, if you enter an array ‘[2, 1, 2, 4, 3]’, you return the array ‘[4, 2, 4, -1, 4]’. With the ring property, ** the last element 3 goes around and finds the element 4 ** larger than itself.
Generally, the ring effect is obtained by modulo (remainder) of the% operator:
1 | int[] arr = {1,2,3,4,5}; |
This problem must still use a monotone stack solution template, but the difficulty lies in, for example, the input is’ [2, 1, 2, 4, 3] ', for the last element 3, how to find element 4 as Next Greater Number.
** For this requirement, the common routine is to double the length of the array **:
In this way, element 3 finds element 4 as the Next Greater Number, and all other elements can be calculated correctly.
With this idea, the simplest way to implement it is of course to construct this double-length array and then apply the algorithm template. However, instead of constructing a new array, we can use the loop array technique to simulate the effect of doubling the length of the array.
Just look at the code directly:
1 | vector<int> nextGreaterElements(vector<int>& nums) { |
In this way, the problem of ring arrays can be cleverly solved with time complexity O (N).
Comprehensive application
After reading the above description, you can take a look at the topic of this comprehensive application; https://leetcode-cn.com/problems/sum-of-subarray-ranges/
1 | typedef struct { |