【LeetCode】栈合集 单调栈合集

232. 用栈实现队列
使用栈实现队列的下列操作:
push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
 
示例:

MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);  
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false
 
说明:
你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
class MyQueue {
    
    
public:
    stack<int> stkin;
    stack<int> stkout;
    /** Initialize your data structure here. */
    MyQueue() {
    
    

    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
    
    
        stkin.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
    
    
        if(stkout.empty()){
    
    
            while(!stkin.empty()){
    
    
                stkout.push(stkin.top());
                stkin.pop();
            }
        }
        int res=stkout.top();
        stkout.pop();
        return res;
    }
    
    /** Get the front element. */
    int peek() {
    
    
    int res=this->pop();
    stkout.push(res);
    return res;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
    
    
        return (stkin.empty()&&stkout.empty());
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

单调栈

明确单调栈能够解决的问题

  1. 找右侧第一个更小 增栈
  2. 找右侧第一个更大 减栈
  3. 找左侧第一个更小 增栈
  4. 找左侧第一个更大 减栈
单调栈就是维护一个单调递增或者递减的栈,用来得到最终结果。
寻找下一个最大元素或者前一个比自身大的元素。
可以用哈希表匹配
也可以用栈存下标来计算距离
或者两个栈来一个存放单调栈,一个维护下标
通常用于数组的比较。

模板

 for(int i=0;i<nums2.size();i++){
    
    
            while(!is.empty()&&nums2[i]>is.top()){
    
    
                um[is.top()]=nums2[i];
                is.pop();
            }
            is.push(nums2[i]);
        }    

581. 最短无序连续子数组

581. 最短无序连续子数组
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

你找到的子数组应是最短的,请输出它的长度。

示例 1:

输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
说明 :

输入的数组长度范围在 [1, 10,000]。
输入的数组可能包含重复元素 ,所以升序的意思是<=。
通过次数38,620提交次数110,484
class Solution {
    
    
public:
    int findUnsortedSubarray(vector<int>& nums) {
    
    
       vector<int> tmp=nums;
       sort(tmp.begin(),tmp.end());
       int left,right;
       for(int i=0;i<nums.size();i++){
    
    
           if(nums[i]!=tmp[i]){
    
    
            left=i;
             break;
           }
          }
       for(int i=nums.size()-1;i>=0;i--){
    
    
           if(nums[i]!=tmp[i]){
    
    
           right=i;
           break;
           } 
       }

       return right>left?(right-left+1):0;
    }
};

402. 移掉K位数字

  • 当我们离开主循环时,我们删除了 m 个数字,这比要求的要少,即(m<k)。在极端情况下,我们不会删除循环中单调递增序列的任何数字,即 m==0。在这种情况下,我们只需要从序列尾部删除额外的 k-m 个数字。
  • 一旦我们从序列中删除 k 位数字,可能还有一些前导零。要格式化最后的数字,我们需要去掉前导零。
  • 我们最终可能会从序列中删除所有的数字。在这种情况下,我们应该返回零,而不是空字符串。
402. 移掉K位数字
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
示例 1 :

输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3,2 形成一个新的最小的数字 1219。
示例 2 :

输入: num = "10200", k = 1
输出: "200"
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :

输入: num = "10", k = 2
输出: "0"
解释: 从原数字移除所有的数字,剩余为空就是0。
通过次数26,532提交次数88,444

class Solution {
    
    
public:
    string removeKdigits(string num, int k) {
    
    
        string res;
        for(int i=0;i<num.size();i++){
    
    
            while(k&&!res.empty()&&res.back()>num[i]){
    
    
                res.pop_back();
                k--;
            }
           if (res.size() == 0 && num[i] == '0')
			continue;
            res+=num[i];
        }

        while(k!=0&&!res.empty()){
    
    
            res.pop_back();
            k--;
        }

        return res==""?"0":res;
    }
};

496. 下一个更大元素 I

496. 下一个更大元素 I
给定两个 没有重复元素 的数组 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 。
    
提示
nums1和nums2中所有元素是唯一的。
nums1和nums2 的数组大小都不超过1000。
通过Stack、HashMap解决

先遍历大数组nums2,首先将第一个元素入栈;
继续遍历,当当前元素小于栈顶元素时,继续将它入栈;当当前元素大于栈顶元素时,栈顶元素出栈,此时应将该出栈的元素与当前元素形成key-value键值对,存入HashMap中;
当遍历完nums2后,得到nums2中元素所对应的下一个更大元素的hash表;
遍历nums1的元素在hashMap中去查找‘下一个更大元素’,当找不到时则为-1
class Solution {
    
    
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
    
    
        stack<int> is;
        unordered_map <int,int> um;
        vector<int> res;
        for(int i=0;i<nums2.size();i++){
    
    
            while(!is.empty()&&nums2[i]>is.top()){
    
    
                um[is.top()]=nums2[i];
                is.pop();
            }
            is.push(nums2[i]);
        }    
        for(int i=0;i<nums1.size();i++){
    
    
            if(um.find(nums1[i])!=um.end())
            res.push_back(um[nums1[i]]);
            else res.push_back(-1);
        }
        return res;
    }
};

739. 每日温度 和下一个更大元素很相似 找下一个最大的值(或者下标)

因为是找右边大的数 因此维护的是递减栈 当当前元素小于栈顶时压入,等待大数,大于时找到该数,弹出栈顶,直到小于栈顶元素,压入栈顶

739. 每日温度
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

通过次数103,053提交次数159,408
class Solution1 {
    
    
public:
//单调栈的方法
    vector<int> dailyTemperatures(vector<int>& T) {
    
    
        vector<int> res(T.size(),0);//初始化大小即可当数组赋值
        unordered_map<int,int> um;
        stack<int> stk;
        //放入哈希表 存下标 用于算距离  当遇到一样的数字的时候会解答出错
        for(int i=0;i<T.size();i++){
    
    
             um[T[i]]=i;
             while(!stk.empty()&&stk.top()<T[i]){
    
    
                 res[um[stk.top()]]=i-um[stk.top()];//当小的时候存入um[stk.top()]下标的答案
                 stk.pop();//当前元素对应答案已经找到
             }
             //当当前元素比栈顶小的时候,元素压入栈
             stk.push(T[i]);
        }
       return res;
    }
};

class Solution {
    
    
public:
//单调栈的方法
    vector<int> dailyTemperatures(vector<int>& T) {
    
    
        vector<int> res(T.size(),0);//初始化大小即可当数组赋值
        
        stack<int> stk;
        //栈存下标
        for(int i=0;i<T.size();i++){
    
    
            
             while(!stk.empty()&&T[stk.top()]<T[i]){
    
    
                 res[stk.top()]=i-stk.top();//当小的时候存入um[stk.top()]下标的答案
                 stk.pop();//当前元素对应答案已经找到
             }
             //当当前元素比栈顶小的时候,元素压入栈
             stk.push(i);
        }
       return res;
    }
};

901. 股票价格跨度 单调栈维护一个单调递减的栈

因为是往回找大数,因此维护一个递减的栈,如果当前数比栈顶元素大,则表示此数之后的答案都不会是栈顶元素,因此需要把栈顶元素替换,然而还需要存储间距,就把当前栈顶元素的间距 加上替换元素的间距,如果比栈顶元素还小,则直接返回1,没有比这更小的数了。

901. 股票价格跨度
编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。

今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。

 

示例:

输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1,
S.next(80) 被调用并返回 1,
S.next(60) 被调用并返回 1,
S.next(70) 被调用并返回 2,
S.next(60) 被调用并返回 1,
S.next(75) 被调用并返回 4,
S.next(85) 被调用并返回 6。

注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。
class StockSpanner {
    
    
public:
    stack<int> prices;
    stack<int> weight;
    StockSpanner() {
    
    

    }
    
    int next(int price) {
    
    
        int w=1;
        while(!prices.empty()&&price>=prices.top()){
    
    
            prices.pop();
            w+=weight.top();
            weight.pop();
        }
          prices.push(price);
          weight.push(w);

        //当小的时候就返回1
        return w;
        
    }
};

84. 柱状图中最大的矩形 右边最小 递增栈 pop掉当前栈顶,下一个元素就是左边较矮

在这里插入图片描述

84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为1。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:

输入: [2,1,5,6,2,3]
输出: 10

// 利用单调递增栈,求解以每个数组元素作为高度的矩形的面积,选择最大值即可
// 由于单调递增栈可以直接找到元素左侧和右侧第一个更小的元素,因此无需两个栈,一个栈即可解决问题
// 由于要计算矩形宽度,因此下标入栈而非数组元素入栈

class Solution1 {
    
    
public:
    int largestRectangleArea(vector<int>& heights) {
    
    
//暴力 最后一个用例不通过
        int len=heights.size();
        if(len==0)
        return 0;
        int ans=INT_MIN;
        for(int i=0;i<len;i++){
    
    

            int left=i-1;
            int right=i+1;
            while(left>=0&&heights[left]>=heights[i])
            left--;
            while(right<=len-1&&heights[right]>=heights[i])
            right++;
            left++;
            right--;
            int dis=right-left+1;
            int area=dis*heights[i];
            ans=ans>area?ans:area;
        }
        return ans;
    }
};

class Solution {
    
    
public:
    int largestRectangleArea(vector<int>& heights) {
    
    
        heights.push_back(0);
        stack<int> stk;
        int maxarea=0;
        for(int i=0;i<heights.size();i++){
    
    
            while(!stk.empty()&&heights[i]<=heights[stk.top()]){
    
    
                int top=stk.top();
                stk.pop();

                maxarea=max(maxarea,(stk.empty()?(i):(i-stk.top()-1))*heights[top]);
                
            }
            stk.push(i);
        }
        return maxarea;
    }
};

239. 滑动窗口最大值

239. 滑动窗口最大值
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。

进阶:
你能在线性时间复杂度内解决此题吗?

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 
  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
class Solution {
    
    
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    
    
        vector<int> res;
        deque<int> deque;
        for(int i=0;i<nums.size();i++){
    
    
            while(!deque.empty()&&deque.front()==i-k)//队列存储的是下标
            //当当前元素下标和栈顶最大元素的下标相差k时,弹出栈顶
            deque.pop_front();
            //当当前元素比窗口内的后面的元素大时,弹出栈尾
            while(!deque.empty()&&nums[i]>nums[deque.back()])
            deque.pop_back();
            deque.push_back(i);
            if(i>=k-1) res.push_back(nums[deque.front()]);
        }
        return res;
    }
};

更多栈的练习
225. 用队列实现栈
28. 实现 strStr()
150. 逆波兰表达式求值

猜你喜欢

转载自blog.csdn.net/qq_37581730/article/details/108574346
今日推荐