一道Medium,两道Hard带你刷爆力扣单调栈(模板解题,学不会来捶我,建议收藏)

Posted Mancuoj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一道Medium,两道Hard带你刷爆力扣单调栈(模板解题,学不会来捶我,建议收藏)相关的知识,希望对你有一定的参考价值。

Preface

继上篇【面试高频】详解单调栈,今天我们来讲述一下如何用我给出的单调栈模板刷题。

所谓单调栈其实就是栈内元素具有单调性, 常用于解决下一个更大元素 这种问题。

我们会讲一下其中最具代表性的三道题,其中一道Medium,两道Hard,保证你看完一定有所收获:

 // 单调栈模板,如果不懂可以看我的上篇文章
 for (遍历这个数组) {
     while (栈不为空 && 栈顶元素与当前数组元素比较) {
         出栈;
     }
     入栈; 
 }

739. 每日温度

Description

Simulation

思路:我们先列一个表格分析一下示例数据是怎么来的

第1天第2天第3天第4天第5天第6天第7天第8天
温度7374757169727673
下一个更高的温度第2天第3天第7天第6天第6天第7天
天数差11421100

首先想到的当然是暴力解法,两层 for 循环挨个遍历一遍,计算天数差即可,时间复杂度O(n^2)。

get到暴力解法也很重要,毕竟不是什么时候都能想到最优解,而且我们还可以基于暴力解法进行下一步优化。

题目中要求计算下一个更高的温度的天数差,其实就是要找到自己右边第一个比自己大的元素, 然后计算下标之差

很明显要用到一个单调减(栈头元素最小)的栈,通过O(n)的时间复杂度(即遍历一遍)就能找到每个元素的右边第一个比它大的元素,本质就是空间换时间。

  1. 为什么是单调减?

    答:因为只有递减的时候,我们才能得到天数差,如74放入栈时,73即可出栈,第一天的天数差就得到了。

  2. 怎么计算天数差?

    答:单调栈内只存放元素的下标即可,需要数值比较时直接使用 t[i] 即可。

  3. 模板怎么用?⏬

既然已经分析出要用单调栈,我们可以直接把三板斧(模板)使出来:

 // 模板
 stack<int> stk;
 for (int i = 0; i < t.size(); i++) {                // 遍历温度数组t
     while (!stk.empty() && t[i] > t[stk.top()]) {   // 出栈条件,保持单调减
         stk.pop();
     }
     stk.push(i);                                    // 入栈,放入下标
 }

在脑中模拟一下出入栈的过程:

  • 栈为空,73的下标0入栈,栈内元素 0

  • 74 > 73,73出栈74入栈,天数差为1,栈内元素 1

  • 75 > 74,74出栈75入栈,天数差为1,栈内元素 2

从第二步就可以看出来,我们需要在栈内元素出栈前通过下标计算天数差,增加一行即可:

     while (!stk.empty() && t[i] > t[stk.top()]) {   // 出栈条件,保持单调减
         res[stk.top()] = i - stk.top();    // 添加计算
         
         stk.pop();
     }

Solution

完整代码如下,可以尝试一下,原题链接

 // hrh 8/28 
 class Solution {
 public:
     vector<int> dailyTemperatures(vector<int>& t) {
         vector<int> res(t.size());
         stack<int> stk;for (int i = 0; i < t.size(); i++) {                // 遍历
             while (!stk.empty() && t[i] > t[stk.top()]) {   // 出栈条件
                 res[stk.top()] = i - stk.top();             // 计算天数差值
                 stk.pop();                                  // 保持单调减
             }
             stk.push(i);                                    // 入栈
         }return res;
     }
 };

总结一下解题过程:

  1. 判断这道题能否用单调栈解决
  2. 判断单调性,给出三板斧
  3. 模拟计算过程,删改模板得到结果

42. 接雨水

Description

Simulation

思路:

  1. 看图分析,只有下一个大于等于自己的元素插入才会形成凹槽接到雨水。所以显而易见,我们应该使用单调栈。
  2. 单调性怎么判断?很明显,比自己小的可以直接插入,大的插入要让前面的小元素出栈,因此我们需要一个递减的栈(栈顶元素最小)。
  3. 面积怎么计算?底 ✖ 高大伙都知道,然后按行计算,下面是分析:

首先明确一下单调栈里到底要存什么内容,每个柱的高度?不不不!我们需要下标来计算宽度,而下标又可以直接get到对应的高度,所以只需一个单调栈来存储下标就可以了。

画了一张图进行模拟计算过程,一组数[2,1,0,3] 从左到右依次入栈:

  • 2入栈,栈为空,下标i直接入栈,栈内元素 i
  • 1<2,直接入栈,栈内元素 i, i+1
  • 0<1,直接入栈,栈内元素 i, i+1, i+2
  • 3>0,保存0作为基底然后出栈,计算凹槽面积:高为1,选择3与当前栈顶元素1之间小的那个(木桶理论,看短的);底为1,3的下标减去1的下标 (i+3) - (i+2) ;总面积+1,继续判断 👇
  • 3>1,保存1作为基底然后出栈,计算凹槽面积:高为1,因为1作为基底,此时栈顶元素为2,所以高为 2-1=1 ,下标计算出底为2;总面积+2,继续判断 👇
  • 3>2,2出栈,但此时栈为空,不需要继续计算。3终于入栈,最后栈内元素 i+3
  • 总面积为3

看完模拟过程,相信你已经有了思路,单调减的栈,三板斧一整,我们只需要在出栈的时候计算面积即可。下面给出完整代码 🍉🍉🍉

Solution

 // hrh 8/29 
 class Solution {
 public:
     int trap(vector<int>& h) {
         stack<int> stk;    
         int res = 0;
         
         // 三板斧!
         for (int i = 0; i < h.size(); i++) {
             while (!stk.empty() && h[i] >= h[stk.top()]) {
                 int base = h[stk.top()];        // 保存基底然后出栈
                 stk.pop();      
                 
                 // 栈不空时计算面积
                 if (!stk.empty()) {             
                     int height = min(h[stk.top()], h[i]) - base;
                     int width = i - stk.top() - 1;
                     res += height*width;
                 }
             }
             
             stk.push(i);
         }return res;
     }
 };

84. 柱状图中最大的矩形

Description

Simulation

思路分析:

  1. 大眼一瞧,柱状图高高低低,计算面积,这不是接雨水吗?单调栈确认!

  2. 判断单调性?可以看到图中都是遇到小值需要出栈的时候才计算面积,可以确认是递增的栈,也就是栈顶元素最大。单调性确定!直接三板斧给出:

  3. 单调栈存入下标计算宽度,根据出栈的最小值计算高度(具体思路上篇文章已经详细给出,在此不赘述)删改后可得:

for (int i = 0; i < h.size(); i++) {
    
    while (!stk.empty() && h[i] < h[stk.top()]) {	// 出栈条件
        int pre = stk.top();	// 保存要出栈的元素
        stk.pop();

        if (!stk.empty()) {		// 计算面积
            int height = h[pre];
            int width = i - stk.top() - 1;
            res = max(res, height*width);	// 保留更大的值
        }
    }
    
    stk.push(i);
}

但这样就足够了吗?显然不行,设想两种情况:

  1. 输入两个元素[2,5],本身递增可以直接入栈,没有出栈怎么计算面积?
  2. 输入任意一个元素,怎么计算面积?

其实解决方法很简单,我们只要在数组前后各加一个0就可以了。0一定是最小元素,在前面加一个0不用担心栈空的情况,每次出栈都计算一下面积;没有出栈条件就创造出栈条件,所以在数组后面加一个0。

h.insert(h.begin(), 0);		// 前插一个0
h.push_back(0);			    // 后插一个0

Solution

完整代码如下:

class Solution {
public:
    int largestRectangleArea(vector<int>& h) {
        stack<int> stk;
        int res = 0;
        h.insert(h.begin(), 0);
        h.push_back(0);

        for (int i = 0; i < h.size(); i++) {
    
            while (!stk.empty() && h[i] < h[stk.top()]) {	// 出栈条件
                int pre = stk.top();	// 保存要出栈的元素
                stk.pop();

                // 栈不会空
                int height = h[pre];
                int width = i - stk.top() - 1;
                res = max(res, height*width);	// 保留更大的值
            }
            
            stk.push(i);
        }
    
        return res;
    }

};

Conclusion

复习一下解题过程:

  1. 判断这道题能否用单调栈解决
  2. 判断单调性,给出三板斧
  3. 模拟计算过程,删改模板得到结果

可以看到最大矩形面积这道题跟接雨水虽然相似,但麻烦了许多,特殊值的输入给我们的解题思路带来了一定困扰,不过你不能指望Hard题都是套个模板就能AC的。

虽然三板斧很好用,但一些特殊条件的计算仍然是需要你自己的判断,那么问题来了:怎么提高自己的能力?

我这里有好康的:力扣单调栈题库

刷题!刷题!还是TMD刷题!!!🍗🍗


写文不易,求个赞 💗💗

可以点个关注互相交流~

我是Mancuoj,更多有趣文章:我的个人主页

以上是关于一道Medium,两道Hard带你刷爆力扣单调栈(模板解题,学不会来捶我,建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章

leetcode刷题之消失的数字(超详解刷爆力扣)

手把手带你刷LeetCode——20.一道笔试题

7天带你全方位刷爆数据结构与算法,每天一道,高效刷题

手把手带你刷好题——29.从大到小输出(非力扣,作业)

手把手带你刷好题—— 36.关键字const具体用法(作业非力扣)

手把手带你刷好题—— 35.水仙花数(鹏哥的写法)(作业非力扣)