大厂高频算法考点--单调栈

什么是单调栈:

单调栈就是借助一个栈,在仅仅使用当前栈的条件下,时间复杂度是N(n),将每个节点最有离这他最近的大于或者是小于的数据返回,将已知数组的元素放到栈里。再自我实现的代码里面我们使用数组实现栈结构,将我们的运算时间再次优化。

二.代码实现单调栈:

这个测试链接是牛客网的测试,大家可以再学习之后就测试一下。

https://www.nowcoder.com/practice/2a2c00e7a88a498693568cef63a4b7bb

java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    private static int n, m,
            r; //n 代表数组的长度      之后的值就是数组的值  r是为了节约栈的空间,使用数组
    private static int[] arr = new int[1000001];
    private static int[] stack = new
    int[1000001];//栈里面仿制的是数组的下标
    private static int[][] ans = new int[1000001][2];
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //不是读取一行
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while (in.nextToken() != StreamTokenizer.TT_EOF) {
            n = (int)in.nval;
            for (int i = 0; i < n; i++) {
                in.nextToken();
                arr[i] = (int)in.nval;
            }
            compute();//开始编写核心逻辑
            for (int i = 0; i < n; i++) {
                out.println(ans[i][0] + " " + ans[i][1]);
            }
        }
        out.flush();
        out.close();
        br.close();
    }

    // arr[0...n-1]
    public static void compute() {
        r = 0;
        int cur;
        // 遍历阶段
        for (int i = 0; i < n; i++) {
            while(r>0&&arr[stack[r-1]]>=arr[i]){
                cur=stack[--r];
                ans[cur][1]=i;
                ans[cur][0]=r>0?stack[r-1]:-1;
            }
            stack[r++]=i;
        }
        // 清算阶段
        while (r > 0) {
                cur=stack[--r];
                ans[cur][1]=-1;
                ans[cur][0]=r>0?stack[r-1]:-1;
        }

        // 修正阶段
        // 左侧的答案不需要修正一定是正确的,只有右侧答案需要修正
        // 从右往左修正,n-1位置的右侧答案一定是-1,不需要修正
        for (int i = n - 2; i >= 0; i--) {
            if (ans[i][1] != -1 &&
                    arr[ans[i][1]] == arr[i]) { 
                ans[i][1] = ans[ans[i][1]][1];
            }
        }
    }
}

三.单调栈类型题总结

3.1 天气状况

这是leetcode上面的一个题,测试连接如下:739. 每日温度 - 力扣(LeetCode)

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 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]

这是题目的描述,我们可以尝试就是使用单调栈解决。答案如下:

java 复制代码
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        //创建答案数组
         int[] answer =new int[temperatures.length];
        //创建一个数组,实现栈的功能,注意这个栈实现的功能是只有比他大的时候出战
         int[] stack =new int[temperatures.length];
         int r=0;
        for(int i=0;i<temperatures.length;i++){
                 
                int cur=0;
              while(r>0&&temperatures[stack[r-1]]<temperatures[i]){
                cur=stack[--r];
                answer[cur]=i-cur;
              }
              stack[r++]=i;
        }
        return answer; 
    }
    
}

3.2:907. 子数组的最小值之和 - 力扣(LeetCode)

复制代码
就是查找比它小的数在哪里,那他就是当前数组里面最小的值了  
​
主要就是这个值在哪里呢。【1 3 4 5 2 5 7 1 2 3】
                    【0 1 2 3 4 5 6 7 8 9】总结公式  
​
主要的优化就是即使是相等的也不需要总结这个单调栈。因为他缺的值在后续中会补上
​
【1 3 4 5 6 5 7 1 2 3】
​
【0 1 2 3 4 5 6 7 8 9】  
java 复制代码
class Solution {
    public static int MOD = 1000000007;
​
    public int sumSubarrayMins(int[] arr) {
        //创建栈,开始计算结果
        int[] stack =new int[arr.length];
        int r=0;
        int cur=0;
        int[][] ans=new int[arr.length][2];
        for(int i=0;i<arr.length;i++){
            while(r>0&&arr[stack[r-1]]>arr[i]){
                cur=stack[--r];
                ans[cur][1]=i;
                ans[cur][0]=r>0?stack[r-1]:-1;
            }
            stack[r++]=i;
        }
        while(r>0){
            cur=stack[--r];
            ans[cur][1]=arr.length;
            ans[cur][0]=r>0?stack[r-1]:-1;
        }
        long sum=0;
        for(int i=0;i<ans.length;i++){
            //加法的取模运算没什么特殊的
            sum=(sum+(long)((long)arr[i]*(long)(i-ans[i][0])*(long)(ans[i][1]-i)))%MOD;
        }
        return (int)sum;
    }
}

方法二:

java 复制代码
class Solution {
    public static int MOD = 1000000007;
​
    public int sumSubarrayMins(int[] arr) {
        //创建栈,开始计算结果
        int[] stack =new int[arr.length];
        int r=0;
        int cur=0;
        long sum=0;
        for(int i=0;i<arr.length;i++){
            while(r>0&&arr[stack[r-1]]>arr[i]){
                cur=stack[--r];
                int right =i;
                int left=r>0?stack[r-1]:-1;
                //加法的取模运算没什么特殊的
                sum=(sum+(long)((long)arr[cur]*(long)(cur-left)*(long)(right-cur)))%MOD;
            }
            stack[r++]=i;
        }
        while(r>0){
            cur=stack[--r];
            int right =arr.length;
            int left=r>0?stack[r-1]:-1;
             sum=(sum+((long)arr[cur]*(long)(cur-left)*(long)(right-cur)))%MOD;
        }
        return (int)sum;
    }
}
复制代码
执行用时分布
6ms
击败
98.32%

3.3:84. 柱状图中最大的矩形 - 力扣(LeetCode)

java 复制代码
class Solution {
    public int largestRectangleArea(int[] heights) {
        //现在思考一个问题,就是这个的时间复杂度是O(N)
        int[] stack =new int[heights.length];
        int r=0;int cur=0;
        int ans=0;
        for(int i=0;i<heights.length;i++){
            while(r>0&&heights[stack[r-1]]>=heights[i]){
                cur =stack[--r];
                int left=r>0?stack[r-1]:-1;
                int right=i;
                ans=Math.max((right-left-1)*heights[cur],ans);
            }
            stack[r++] = i; // 把当前柱子的索引入栈
        }
        while(r>0){
            cur =stack[--r];
            int left=r>0?stack[r-1]:-1;
            int right=heights.length;
             ans=Math.max((right-left-1)*heights[cur],ans);
        }
        return ans;
    }
}

3.4:85. 最大矩形 - 力扣(LeetCode)

思路:

复制代码
首先在我们的思路里面,以每一层为底去计算面积
但是必须是连续的才会是累计关系,如果是零的话就直接为0  
解释一下为什么是这样的,因为如果是0的话,你这里是组不成长方形的,所以你的结果就会和你以前计算的值相同,在这里算的话就是零,但是这是零的话,对别的点是否有影响呢,当然是不会有的
因为你这里是零,谁也不能在这里创建长方形了,不会由影响
用到的技巧是压缩数组
java 复制代码
class Solution {
    public int maximalRectangle(char[][] matrix) {
         int ans = 0;
        int[] arr = new int[matrix[0].length]; // 初始化高度数组
        
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                // 更新 arr 数组:如果是 '1' 则增加高度,否则重置为 0
                arr[j] = (matrix[i][j] == '1') ? arr[j] + 1 : 0;
            }
            ans = Math.max(getSum(arr), ans); // 更新答案
        }
        return ans;
    }
    private int getSum(int[] heights){
         //现在思考一个问题,就是这个的时间复杂度是O(N)
        int[] stack =new int[heights.length];
        int r=0;int cur=0;
        int ans=0;
        for(int i=0;i<heights.length;i++){
            while(r>0&&heights[stack[r-1]]>=heights[i]){
                cur =stack[--r];
                int left=r>0?stack[r-1]:-1;
                int right=i;
                ans=Math.max((right-left-1)*heights[cur],ans);
            }
            stack[r++] = i; // 把当前柱子的索引入栈
        }
        while(r>0){
            cur =stack[--r];
            int left=r>0?stack[r-1]:-1;
            int right=heights.length;
             ans=Math.max((right-left-1)*heights[cur],ans);
        }
        return ans;
    }
}
相关推荐
咸鱼鱼不翻身2 分钟前
Java面试题库——MySQL
java·mysql·面试
昨天今天明天好多天2 分钟前
【Mac】Python 环境管理工具
开发语言·python·macos
西瓜本瓜@9 分钟前
在Android开发中实现静默拍视频
android·java·开发语言·学习·音视频
_.Switch14 分钟前
高效网络自动化:Python在网络基础中的应用
运维·开发语言·网络·python·数据分析·自动化
2301_80220193关注我17 分钟前
Parameters参数、增删改标签、事务、typeAliases
java·数据库·windows
清灵xmf23 分钟前
JavaScript 中如何识别异步函数?
开发语言·javascript·async·异步函数
听潮阁37 分钟前
【SSM详细教程】-13-SpringMVC详解
java·spring boot·spring·java-ee·tomcat·maven·mybatis
七月巫山晴39 分钟前
QChart中柱形图的简单使用并实现【Qt】
开发语言·数据结构·c++·qt·算法·排序算法
阿丁小哥42 分钟前
【Python各个击破】numpy
开发语言·python·numpy