单调栈分析

无重复元素的单调栈

  • 保证元素在入栈的过程中,做到任意时刻从栈底到栈顶的元素都是单调(递增或者递减)的,那么此种栈就是一个单调栈。
  • 单调栈的性质:
    • 利用其找到数组中左边最近且小于当前元素的元素右边最近且小于当前元素的元素
    • 利用其找到数组中左边最近且大于当前元素的元素右边最近且大于当前元素的元素
  • 单调栈找到小于(大于)且最近的元素的原理:
    • 栈中存的是数组元素的下标
    • 递增单调栈中(栈底到栈顶按照从小到大的顺序)
      • 当前元素如果大于栈顶,或者栈为空 入栈
      • 当前元素如果小于栈顶,一直退栈直到当前元素大于栈顶才能入栈
        • 退栈栈顶元素的时候,此时就是计算此退栈元素的左小且最近右小且最近的时刻
        • 让其退栈的右边元素就是其右小且最近,若无则是-1(无效下标)
        • 其退栈后,新的栈顶就是其左小且最近,若无则是-1(无效下标)
      • 最后将所有元素遍历完成后,必须按照同样的方法去将栈中剩余元素全部退栈掉
void getNearNums(const vector<int>& nums, vector<pair<int, int>>& nearMinNums)
{
  stack<int> stIdx; // 单调栈 
  for (int i = 0; i < nums.size(); ++i) {
    if (stIdx.empty() || nums[i] > nums[stIdx.top()]) {
      stIdx.push(i); // 存下标
    } else {
      //栈不为空,跟栈顶比较,只要这个栈顶还大于当前元素就要继续退栈
      while (!stIdx.empty() && nums[i] < nums[stIdx.top()]) {
        int idx = stIdx.top();
        stIdx.pop();
        // first左小  second右小
        nearMinNums[idx].first = stIdx.empty() ? (-1) : (stIdx.top());
        nearMinNums[idx].second = i;
      }
      // 最后必须将当前元素给入栈
      stIdx.push(i);
    }
  }
  // stIdx还有元素
  while (!stIdx.empty()) {
    int idx = stIdx.top();
    stIdx.pop();
    nearMinNums[idx].first = stIdx.empty() ? (-1) : (stIdx.top());
    nearMinNums[idx].second = -1;
  }
}

有重复元素的单调栈

  • 基本的思路跟上面的单调栈还是相同的,不过有一些区别
  • 入栈时,栈中只有一个指针指向一个链表,链表中按照从头到尾的顺序在尾部插入相同值的元素的数组中的下标
  • 出栈时,左小且最近元素的下标为新栈顶的链表的最右元素的下标 或 -1
    • 特别注意, 弹栈的时候弹栈的是整个链表,然后在整个链表上进行一个遍历
    • 同样的,弹栈的时刻就是当前被弹栈元素的计算时刻。

单调栈实战

直方图的最大矩形问题

题目连接
思路:
最大的那个面积肯定是取决于某个最低的高度值,必然是某个高度值。因此,我们只需要计算出每个高度能得到的最大面积,然后找到最大的面积就是最终答案。如何求每个矩形能达到的最大面积呢?利用递归栈可以分别找到左、右最小最近的元素的下标,因此相减不就是当前矩形的最大宽度吗!

class Solution {
public:
    // 最大的那个面积肯定是取决于某个最低的高度值,必然是某个高度值,
    // 因此,我们只需要计算出每个高度能得到的最大面积,然后找到最大的面积就是最终答案
    unsigned long long largestRectangleArea(vector<int>& heights) {
        long maxArea = 0;
        stack<int> stIdx;
        for (int i = 0; i < heights.size(); ++i) {
          if (stIdx.empty() || heights[stIdx.top()] <= heights[i]) {
            stIdx.push(i);
          } else if (heights[stIdx.top()] > heights[i]) {
            while (!stIdx.empty() && heights[stIdx.top()] > heights[i]) {
              // 弹栈
              int index = stIdx.top();
              stIdx.pop();
              // 计算面积
              int height = heights[index];
              // 宽度就是最右边的那个近小值-最左边的近小值
              int width = (i) - (stIdx.empty() ? (-1) : (stIdx.top())) - 1;
              unsigned long long area = height * width;
              maxArea = area > maxArea ? area : maxArea;
            }
            stIdx.push(i);
          }
        }

        while (!stIdx.empty()) {
          int index = stIdx.top();
          stIdx.pop();
          // 计算面积
          int height = heights[index];
          // 宽度就是最右边的那个近小值-最左边的近小值
          int width = (heights.size()) - (stIdx.empty() ? (-1) : (stIdx.top())) - 1;
          long area = height * width;
          maxArea = area > maxArea ? area : maxArea;
        }
        return maxArea;
    }
};

leetcode反馈

参考资料

1 https://blog.csdn.net/weixin_40374341/article/details/100055210
2 https://blog.csdn.net/weixin_40374341/article/details/100065437

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值