单调栈常用算法

1.单调栈

除了单调栈最经典的用法之外,在很多问题里单调栈还可以 维持求解答案的可能性

1)单调栈里的所有对象按照 规定好的单调性来组织

2)当某个对象进入单调栈时, 会从 栈顶开始 依次淘汰单调栈里 对后续求解答案没有帮助 的对象

3)每个对象从栈顶弹出的时 结算当前对象参与的答案,随后这个对象 不再参与后续求解答案的过程

4)其实是 先有对题目的分析!进而发现单调性,然后利用 单调栈的特征 去实现

单调栈结构(进阶)

时间限制:2秒 空间限制:256M

知识点单调栈

warning

校招时部分企业笔试将禁止编程题跳出页面,为提前适应,练习时请使用在线自测,而非本地IDE。

描述

给定一个可能含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。

输入描述:

第一行输入一个数字 n,表示数组 arr 的长度。 以下一行输入 n 个数字,表示数组的值

输出描述:

输出n行,每行两个数字 L 和 R,如果不存在,则值为 -1,下标从 0 开始。

示例1

输入:

7
3 4 1 5 6 2 7

复制

输出:

-1 2
0 2
-1 -1
2 5
3 5
2 -1
5 -1

复制

备注:

1≤𝑛≤10000001≤n≤1000000
−1000000≤𝑎𝑟𝑟𝑖≤1000000−1000000≤arri≤1000000

​
//改成Main
public class Main {
​
    static int [] stack  = new int[1000001];
​
    static int right = 0;
​
    static int [][] ans = new int[1000001][2];
​
    static int [] arr = new int[1000001];
​
    static int n = 0;
    public static void main(String[] args) throws IOException{
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String [] str = bf.readLine().split(" ");
        n = Integer.parseInt(str[0]);
        String [] s = bf.readLine().split(" ");
        right = 0;
        int current = 0;
        for(int i = 0;i<n;i++){
            arr [i] = Integer.parseInt(s[i]);
            //先查看栈里是否有东西,并且是否小于栈顶
            while(right > 0&& arr[i] <= arr[stack[right-1]]){
                //弹出栈顶
                current = stack[--right];
                //栈顶元素压着的作为比current小的最近的数
                ans[current][0] = right > 0 ? stack[right-1]:-1;
                //弹出栈顶的作为右边比current小的数
                ans[current][1] = i;
            }
            stack[right++] = i;
        }
        //遍历结束之后
        //清算阶段
        while(right>0){
            int cur = stack[--right];
            ans [cur][0] = right > 0 ? stack[right-1]:-1;
            ans [cur][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];
            }
        }
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        for (int i = 0; i < n; i++) {
            out.println(ans[i][0] + " " + ans[i][1]);
        }
        out.flush();
        out.close();
    }
}

503. 下一个更大元素 II

已解答

中等

给定一个循环数组 numsnums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素

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

示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
public static int[] nextGreaterElements(int[] nums) {
    Deque<Integer> stack = new LinkedList<>();
    int [] ans = new int[nums.length];
    for (int i  = 0;i< nums.length;i++){
        while (!stack.isEmpty()&&nums[stack.peek()]<nums[i]){
            ans[stack.pop()] = nums[i];
​
        }
        stack.push(i);
    }
    int size = stack.size();
    for (int i = 0;i<nums.length;i++){
        while (!stack.isEmpty()&&nums[i]>nums[stack.peek()]){
            ans[stack.pop()] = nums[i];
        }
    }
    while (!stack.isEmpty()){
        ans[stack.pop()] = -1;
    }
    return ans;
}

739. 每日温度

已解答

中等

提示

给定一个整数数组 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]

提示:

  • 1 <= temperatures.length <= 105

  • 30 <= temperatures[i] <= 100

public static int [] answer;
​
public static  int [] stack = new int[10000];
public static int[] dailyTemperatures(int[] temperatures) {
    answer = new int [temperatures.length];
    //指向数组栈的索引
    int r = 0;
    //i指向temperatures的索引
    //遍历整个温度数组
    for (int i=0;i<temperatures.length;i++){
        while (r>0&&temperatures[i]>temperatures[stack[r-1]]){
            r--;
            //因为是i让r弹出,所以i出的温度是离r最近且大于i的
            answer[stack[r]] = i-stack[r];
        }
        //栈中存储的是索引
        stack[r] = i;
        r++;
    }
    return answer;
}

907. 子数组的最小值之和

已解答

中等

给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。

由于答案可能很大,因此 返回答案模 10^9 + 7

示例 1:

输入:arr = [3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。 
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。

示例 2:

输入:arr = [11,81,94,43,3]
输出:444

提示:

  • 1 <= arr.length <= 3 * 104

  • 1 <= arr[i] <= 3 * 104

static int [] stack = new int[30001];
static int  r = 0;
static int MOD = 1000000007;
public static int sumSubarrayMins(int[] arr) {
    r= 0;
    int n  = arr.length;
    long ans = 0;
    for(int i = 0;i<n;i++){
        while(r>0&&arr[stack[r-1]]>=arr[i]){
            int cur = stack[--r];
            if (r==0){
                ans = (ans + (i - cur) *(cur+1)*arr[cur] )%MOD;
            }else {
                ans = (ans + (long) (i - cur) *(cur-stack[r-1])*arr[cur])%MOD;
            }
        }
        stack[r++] = i;
    }
    while(r>0){
        int cur = stack[--r];
        if(r==0){
            ans =  (ans + (long) (n - cur) *(cur+1)*arr[cur])%MOD;
        }else{
            ans = (ans + (long) (n - cur) *(cur-stack[r-1])*arr[cur])%MOD;
        }
    }
    return (int)ans;
}

84. 柱状图中最大的矩形

已解答

困难

相关标签

相关企业

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

img

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

public static int [] stack = new int [100000];
public static int [][] ans = new int[100000][2];
public static int largestRectangleArea(int[] heights) {
    int r = 0;
    int result = 0;
    for (int i = 0;i<heights.length;i++){
        while (r>0&&heights[stack[r-1]]>=heights[i]){
            int cur = stack[--r];
            ans[cur][0] = r>0 ? stack[r-1]:-1;
            ans[cur][1] = i;
        }
        stack[r++] = i;
    }
    while (r>0){
        int cur = stack[--r];
        ans[cur][0] = r>0 ? stack[r-1]:-1;
        ans[cur][1] = heights.length;
    }
    for (int i = heights.length-2;i>=0;i--){
        if (ans[i][1]<heights.length&&heights[ans[i][1]] == heights[i]){
            ans[i][1] = ans[ans[i][1]][1];
        }
    }
    for (int i = 0;i<heights.length;i++){
        result = Math.max(result,heights[i]*(ans[i][1]-ans[i][0]-1));
    }
    return result;
}

坚持完成练习,不懂的可以私信问我

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值