小白专享版单调栈(附图解和多道力扣题解)

Introduction

Monotonic Stack is a special variation of the typical data structure Stack and appeared in many interview questions.

As its name shows, monotonic stack contains all features that a normal stack has and its elements are all monotonic decreasing or increasing.

When to Use Monotonic Stack

Monotonic Stack is the best time complexity solution for many “range queries in an array” problems. Because every element in the array could only enter the monotonic stack once, the time complexity is O(N). (N represents the length of the array).

For every “range query” problem, it could be sure to maintain a range using a normal array/list. However, we will do many repetitive operations to get information from every range. The time complexity is very high and the solution is always not good enough to pass an interview.

Using monotonic stack to maintain a range can save lots of time. 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. Therefore, we don’t need to compare elements one by one again to get minima and maxima in the range. At mean while, because it keeps the elements’ order, we only need to update the stack based on newest adding element.

The concept of monotonic stack, which is just a stack keeping monotonic, is not difficult to understand. But the hardest part is how to use this data structure to model and solve problems. No one will tell you that a problem should use monotonic stack in a real interview. Sometime it’s not obvious which solution is best for a problem if you are not familiar with many techniques and example problems. If a problem is suitable to use monotonic stack, it must has at least three characters:

  1. It is a “range queries in an array” problem.(这是一个“数组中的范围查询”问题。)
  2. The minima/maxima element or the monotonic order of elements in a range is useful to get answer of every range query.(最小值/最大值元素或一个范围内元素的单调顺序对于获得每个范围查询的答案是有用的。)
  3. When a element is popped from the monotonic stack, it will never be used again.(当一个元素从单调堆栈中弹出时,它将永远不会再被使用。)

总结一下基本用法:

(栈内存放的一般是数组元素的下标) 

其实一般用单调栈的情况就是查找下一个大于(或者小于)当前元素的值的元素

原理: 维护栈的单调性  单调递增(从栈头到栈底部是单调递增的) 那么来了一个比栈顶元素大的值 (curnum) curnum就是大于栈顶元素的下一个值

 

出栈的代码

while(!s.empty() && a[i] > s[s.top()])
{
	int topIndex = s.top();
	s.pop();
	//那么topIndex就是下标 它的下一个比他大的num 就是 i对应的数 (分情况处理) 
}

 例题:

496. 下一个更大元素 I

难度简单607

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
    对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
    对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
    对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
    对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
    对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

提示:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 104
  • nums1nums2中所有整数 互不相同
  • nums1 中的所有整数同样出现在 nums2 中
  • class Solution {
    public:
        vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
            vector<int>result;     
            unordered_map<int, int> mp;
            for(int i = 0; i < nums2.size(); i++){
                mp[nums2[i]] = -1;       
            }
            stack<int> s;
            for(int i = 0; i < nums2.size(); i++){      //将所有元素的解更新到hash中
                while(!s.empty() && nums2[i] > nums2[s.top()]){
                    int topIndex = s.top();
                    mp[nums2[topIndex]] = nums2[i];     //下一个更大的元素 就是nums2【i】
                    s.pop();
                }
                s.push(i);      //入栈的是下标
            }
            for(auto each : nums1){
                result.push_back(mp[each]); 
            }
            return result;
        }   
    };

    例题2:

  • 739. 每日温度

    难度中等931

    请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

    示例 1:

    输入: temperatures = [73,74,75,71,69,72,76,73]
    输出: [1,1,4,2,1,1,0,0]
    

    示例 2:

    输入: temperatures = [30,40,50,60]
    输出: [1,1,1,0]
    

    示例 3:

    输入: temperatures = [30,60,90]
    输出: [1,1,0]
    class Solution {
    public:
        vector<int> dailyTemperatures(vector<int>& temperatures) {
            //利用递增单调栈(从栈头到栈底递增)
            stack<int> s;
            int n = temperatures.size();
            vector<int> fins(n, 0);
            s.push(0);      //第一个元素的下标 0 放进去
            for(int i = 1; i < n; i ++){
                while(!s.empty() && temperatures[i] > temperatures[s.top()]){       //如果来了个更大的就将小的踢出去
                    fins[s.top()] =  i - s.top();
                    s.pop();
                }
                s.push(i);      //将当前元素下标进栈
            }
            return fins;
        }
    };

    例题3:(503)

    给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

    示例 1:

    输入: [1,2,1]
    输出: [2,-1,2]
    解释: 第一个 1 的下一个更大的数是 2;
    数字 2 找不到下一个更大的数; 
    第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
    注意: 输入数组的长度不会超过 10000。

    通过次数109,322提交次数171,249

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/next-greater-element-ii
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    class Solution {
    public:
        vector<int> nextGreaterElements(vector<int>& nums) {
            //题目意思就是他是一个循环数组 你如果想要找 1 2 1 
            //                                          * 这个1 的下一个 就从头开始找
            //否则都是从当前的位置开始寻找
            stack<int> s;
            int n = nums.size();
            vector<int> result(n, -1);
            for(int i = 0; i < 2 * n; i++){     //因为是循环数组 两次遍历就足够了 
                while(!s.empty() && nums[i%n] > nums[s.top()]){
                    int topIndex = s.top();
                    result[topIndex] = nums[i % n];       //最终结果存放的就是下标对应的值
                    s.pop();
                }
                s.push(i % n);
            }
            return result;
        }
    };

    例题4:

    接雨水

    难度困难2868

    给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

    示例 1:

    输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
    输出:6
    解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 
    

    示例 2:

    输入:height = [4,2,0,3,2,5]
    输出:9
    

    提示:

    n == height.length 1 <= n <= 2 * 104

    通过次数353,743提交次数602,282

    • 0 <= height[i] <= 105

    class Solution {
    public:
        int trap(vector<int>& height) {
                //利用单调栈维护一个递减单调栈
                //从左向右进行判断 能够形成低洼的话 应该是有左右边界 并且有一个低洼
                //右边界是遍历数组得到的  所以此前stack中应该存储左边界和低洼
                stack<int>stk;
                int fins=0;
                int n=height.size();
                for(int i=0;i<n;i++)
                {
                    ///栈内存放的是每一个柱子的下标
                    while(!stk.empty()&&height[i]>height[stk.top()])
                    {
                        int low=stk.top();      //低洼的高度
        /*标记*/         stk.pop();
                        //出栈后 目前栈顶的元素就是左边界
                        if(stk.empty())
                        {
                            break;
                        }
                        int left=stk.top();     //此时栈顶就是左边界的下标
                        int diatance=i-left-1;      //宽度
                        int hei=min(height[left],height[i])-height[low];        //左右边界的最小值减去low
                        fins+=diatance*hei;
                    }
                    stk.push(i);
                }
                return fins;
        }
    };

おすすめ

転載: blog.csdn.net/qq_52245648/article/details/121445961