单调栈系列1

在这里插入图片描述

单调栈

在一维数组中找第一个满足某种条件的数,这种feel的东西一看就是单调栈

题目1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8zIotwv-1648374578860)(file:///Users/kason/Library/Application%20Support/marktext/images/2022-03-26-12-04-01-image.png)]

/**
  * 题目Id: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 <= 10⁵ 
// 30 <= temperatures[i] <= 100 
// 
// Related Topics 栈 数组 单调栈 👍 1075 👎 0
	
  * 日期:2022-03-26 12:03:08
*/
//给定一个整数数组 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 <= 10⁵ 
// 30 <= temperatures[i] <= 100 
// 
// Related Topics 栈 数组 单调栈 👍 1075 👎 0

package leetcode.editor.cn;

import java.util.Arrays;
import java.util.Stack;
import java.util.stream.Collectors;

public class P739DailyTemperatures {
    public static void main(String[] args) {
        Solution solution = new P739DailyTemperatures().new Solution();
        int[] tmeps = new int[]{73,74,75,71,69,72,76,73};

        int[] ints = solution.dailyTemperatures(tmeps);


        String result = Arrays.stream(ints).mapToObj(Integer::toString).collect(Collectors.joining(","));

        System.out.println("Hello world" + result);
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {

        Stack<Integer> stack = new Stack<>();
        int[] ans = new int[temperatures.length];
        for (int i = 0; i < temperatures.length; i++) {
            if (stack.isEmpty()) {
                stack.push(i);
                continue;
            }
            while (!stack.isEmpty()) {
                Integer top = stack.peek();
                if (temperatures[i] > temperatures[top]) {
                    ans[top] = i - top;
                    stack.pop();
                } else {
                    break;
                }
            }
            stack.push(i);
        }

        while (!stack.isEmpty()) {
            ans[stack.pop()] = 0;
        }

        return ans;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}

更新的解法:

KMP

时间复杂度O(n)

空间复杂度O(1)

该思路由KMP中失配数组的构造演变而来。假设ans[i]记录了i位置上的答案(向右找多少个比自己大),则求ans[i]时,我先看一眼i+1位置,如果T[i+1]比我大,那得了,答案就是它了。

否则我要找的位置至少是比T[i+1]大,那么当然我就看一看ans[i+1]

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        n=len(T)
        ans=[0]*n
        for i in range(n-2,-1,-1):
            now=i+1
            while T[now]<=T[i]:
                if ans[now]:
                    now+=ans[now]
                else:
                    break
            else:
                ans[i]=now-i
        return ans

题目3:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nbHz8K1G-1648374578861)(file:///Users/kason/Library/Application%20Support/marktext/images/2022-03-27-11-43-53-image.png)]

代码:

/**
  * 题目Id:84
  * 题目:柱状图中最大的矩形
  * 内容: //给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 
//
// 求在该柱状图中,能够勾勒出来的矩形的最大面积。 
//
// 
//
// 示例 1: 
//
// 
//
// 
//输入:heights = [2,1,5,6,2,3]
//输出:10
//解释:最大的矩形为图中红色区域,面积为 10
// 
//
// 示例 2: 
//
// 
//
// 
//输入: heights = [2,4]
//输出: 4 
//
// 
//
// 提示: 
//
// 
// 1 <= heights.length <=10⁵ 
// 0 <= heights[i] <= 10⁴ 
// 
// Related Topics 栈 数组 单调栈 👍 1839 👎 0
	
  * 日期:2022-03-27 11:44:47
*/
//给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 
//
// 求在该柱状图中,能够勾勒出来的矩形的最大面积。 
//
// 
//
// 示例 1: 
//
// 
//
// 
//输入:heights = [2,1,5,6,2,3]
//输出:10
//解释:最大的矩形为图中红色区域,面积为 10
// 
//
// 示例 2: 
//
// 
//
// 
//输入: heights = [2,4]
//输出: 4 
//
// 
//
// 提示: 
//
// 
// 1 <= heights.length <=10⁵ 
// 0 <= heights[i] <= 10⁴ 
// 
// Related Topics 栈 数组 单调栈 👍 1839 👎 0

package leetcode.editor.cn;

import java.util.Stack;

public class P84LargestRectangleInHistogram {
    public static void main(String[] args) {
        Solution solution = new P84LargestRectangleInHistogram().new Solution();
        int[] heights = new int[]{2,1,5,6,2,3};
        int i = solution.largestRectangleArea(heights);

        System.out.println("Hello world" + i);
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
        public int largestRectangleArea(int[] heights) {
            Stack<Integer> stack  = new Stack<>();
            int max = 0;
            for (int i = 0; i < heights.length;i++) {
                /*if (stack.isEmpty()) {
                    stack.push(i);
                }*/
                // 结算栈顶
                while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                    max = calArea(heights, stack, max, i);
                }
                stack.push(i);
            }

            // 栈不为空,则数据已经刚好越界,并且全部加到栈里面了。
            while (!stack.isEmpty()) {

                max = calArea(heights, stack, max, heights.length);
                /*int right = heights.length;
                Integer cur = stack.pop();
                int left = stack.isEmpty() ? -1 : stack.peek();
                int area = (right - left - 1) * heights[cur];
                max = Math.max(max, area);*/
            }
            return max;
        }

        private int calArea(int[] heights, Stack<Integer> stack, int max, int i) {
            // 右边界
            int right = i;
            // 当前的结算点
            Integer cur = stack.pop();
            // 左边界
            int left = stack.isEmpty() ? -1 : stack.peek();
            // 面积
            int area = (right - left - 1) * heights[cur];
            max = Math.max(max, area);
            return max;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}

题目4

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WfRGMFl1-1648374578861)(file:///Users/kason/Library/Application%20Support/marktext/images/2022-03-26-15-06-27-image.png)]

/**
 * 题目Id:85
 * 题目:最大矩形
 * 内容: //给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
 * //
 * //
 * //
 * // 示例 1:
 * //
 * //
 * //输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"]
 * //,["1","0","0","1","0"]]
 * //输出:6
 * //解释:最大矩形如上图所示。
 * //
 * //
 * // 示例 2:
 * //
 * //
 * //输入:matrix = []
 * //输出:0
 * //
 * //
 * // 示例 3:
 * //
 * //
 * //输入:matrix = [["0"]]
 * //输出:0
 * //
 * //
 * // 示例 4:
 * //
 * //
 * //输入:matrix = [["1"]]
 * //输出:1
 * //
 * //
 * // 示例 5:
 * //
 * //
 * //输入:matrix = [["0","0"]]
 * //输出:0
 * //
 * //
 * //
 * //
 * // 提示:
 * //
 * //
 * // rows == matrix.length
 * // cols == matrix[0].length
 * // 1 <= row, cols <= 200
 * // matrix[i][j] 为 '0' 或 '1'
 * //
 * // Related Topics 栈 数组 动态规划 矩阵 单调栈 👍 1220 👎 0
 * <p>
 * 日期:2022-03-27 12:28:17
 */
//给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 
//
// 
//
// 示例 1: 
//
// 
//输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"]
//,["1","0","0","1","0"]]
//输出:6
//解释:最大矩形如上图所示。
// 
//
// 示例 2: 
//
// 
//输入:matrix = []
//输出:0
// 
//
// 示例 3: 
//
// 
//输入:matrix = [["0"]]
//输出:0
// 
//
// 示例 4: 
//
// 
//输入:matrix = [["1"]]
//输出:1
// 
//
// 示例 5: 
//
// 
//输入:matrix = [["0","0"]]
//输出:0
// 
//
// 
//
// 提示: 
//
// 
// rows == matrix.length 
// cols == matrix[0].length 
// 1 <= row, cols <= 200 
// matrix[i][j] 为 '0' 或 '1' 
// 
// Related Topics 栈 数组 动态规划 矩阵 单调栈 👍 1220 👎 0

package leetcode.editor.cn;

import java.util.Stack;

public class P85MaximalRectangle {
    public static void main(String[] args) {
        Solution solution = new P85MaximalRectangle().new Solution();
        System.out.println("Hello world");
    }

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int maximalRectangle(char[][] matrix) {
            int max = 0;
            int[] cacheUp = new int[matrix[0].length];
            int[] height = new int[matrix[0].length];
            for (int row = 0; row < matrix.length; row++) {

                for (int col = 0; col < matrix[0].length; col++) {

                    if (matrix[row][col] == '0') {
                        height[col] = 0;
                        cacheUp[col] = 0;
                    } else {
                        height[col] = (matrix[row][col] - '0') + cacheUp[col];
                        cacheUp[col] = height[col];
                    }
                }
                max = Math.max(max, largestRectangleArea(height));
            }
            return max;
        }

        public int largestRectangleArea(int[] heights) {
            Stack<Integer> stack = new Stack<>();
            int max = 0;
            for (int i = 0; i < heights.length; i++) {
                // 结算栈顶
                while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                    max = calArea(heights, stack, max, i);
                }
                stack.push(i);
            }

            // 栈不为空,则数据已经刚好越界,并且全部加到栈里面了。
            while (!stack.isEmpty()) {
                max = calArea(heights, stack, max, heights.length);
            }
            return max;
        }

        private int calArea(int[] heights, Stack<Integer> stack, int max, int i) {
            // 右边界
            int right = i;
            // 当前的结算点
            Integer cur = stack.pop();
            // 左边界
            int left = stack.isEmpty() ? -1 : stack.peek();
            // 面积
            int area = (right - left - 1) * heights[cur];
            max = Math.max(max, area);
            return max;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}

题目5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pd3go3Uq-1648374578861)(file:///Users/kason/Library/Application%20Support/marktext/images/2022-03-27-12-51-07-image.png)]

/**
  * 题目Id:316
  * 题目:去除重复字母
  * 内容: //给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 
//
// 
//
// 示例 1: 
//
// 
//输入:s = "bcabc"
//输出:"abc"
// 
//
// 示例 2: 
//
// 
//输入:s = "cbacdcbc"
//输出:"acdb" 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 10⁴ 
// s 由小写英文字母组成 
// 
//
// 
//
// 注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-
//distinct-characters 相同 
// Related Topics 栈 贪心 字符串 单调栈 👍 693 👎 0
	
  * 日期:2022-03-27 12:51:26
*/
//给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 
//
// 
//
// 示例 1: 
//
// 
//输入:s = "bcabc"
//输出:"abc"
// 
//
// 示例 2: 
//
// 
//输入:s = "cbacdcbc"
//输出:"acdb" 
//
// 
//
// 提示: 
//
// 
// 1 <= s.length <= 10⁴ 
// s 由小写英文字母组成 
// 
//
// 
//
// 注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-
//distinct-characters 相同 
// Related Topics 栈 贪心 字符串 单调栈 👍 693 👎 0

package leetcode.editor.cn;



import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

public class P316RemoveDuplicateLetters {
    public static void main(String[] args) {
        Solution solution = new P316RemoveDuplicateLetters().new Solution();
        //String s = "bbcaac";
        String s = "bcabc";
        //s = "abacb";
        System.out.println("Hello world :  " + solution.removeDuplicateLetters(s));

        s = "cbacdcbc";
        System.out.println("Hello world :  " + solution.removeDuplicateLetters(s));

        s = "abacb";
        System.out.println("Hello world :  " + solution.removeDuplicateLetters(s));
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public String removeDuplicateLetters(String s) {

        Map<Character, Integer> letterCntMap = new HashMap<>();
        Map<Character, Boolean> status = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {

            if (letterCntMap.containsKey(s.charAt(i))) {
                letterCntMap.put(s.charAt(i), letterCntMap.get(s.charAt(i))+1);
            } else {
                letterCntMap.put(s.charAt(i), 1);
            }
        }
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            // 针对一个新的值,之前还没进过栈,那么该值要看看是否比之前的值要小,小则弹出处理
            if (status.get(s.charAt(i)) == null || !status.get(s.charAt(i))) {
                while (!stack.isEmpty()) {
                    Character top = stack.peek();
                    // 当前的值更小,之前的栈里的元素要看看是否要弹出
                    if (s.charAt(i) < top) {
                        if (letterCntMap.get(top) > 1) {
                            // 扔掉一个,后面还有
                            letterCntMap.put(top, letterCntMap.get(top) - 1);
                            status.put(top, false);
                            stack.pop();
                        } else {
                            break;
                        }
                    } else if (s.charAt(i) == stack.peek()) {
                        letterCntMap.put(top, letterCntMap.get(top) - 1);
                        break;
                    } else {
                        break;
                    }
                }
                stack.push(s.charAt(i));
                status.put(s.charAt(i), true);
            } else {
                letterCntMap.put(s.charAt(i), letterCntMap.get(s.charAt(i)) - 1);
            }

        }

        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.reverse().toString();

    }
}
//leetcode submit region end(Prohibit modification and deletion)

}

题目6

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLtc0Z6F-1648374578865)(file:///Users/kason/Library/Application%20Support/marktext/images/2022-03-27-17-45-44-image.png)]

对于两个相同长度的数字序列,最左边不同的数字决定了这两个数字的大小,例如,对于 A = 1axxxA=1axxx,B = 1bxxxB=1bxxx,如果 a > b 则 A > B。

基于此,我们可以知道,若要使得剩下的数字最小,需要保证靠前的数字尽可能小。

因此我们使用单调栈来维护数组的有序性。

一旦遇见s[i+1] < s[i]的时候,这时候如果我们还能删除数字那么我们就应该尝试去删除它。

代码:

/**
  * 题目Id:402
  * 题目:移掉 K 位数字
  * 内容: //给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 
// 
//
// 示例 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 。
// 
//
// 
//
// 提示: 
//
// 
// 1 <= k <= num.length <= 10⁵ 
// num 仅由若干位数字(0 - 9)组成 
// 除了 0 本身之外,num 不含任何前导零 
// 
// Related Topics 栈 贪心 字符串 单调栈 👍 758 👎 0
	
  * 日期:2022-03-27 17:20:09
*/
//给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 
// 
//
// 示例 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 。
// 
//
// 
//
// 提示: 
//
// 
// 1 <= k <= num.length <= 10⁵ 
// num 仅由若干位数字(0 - 9)组成 
// 除了 0 本身之外,num 不含任何前导零 
// 
// Related Topics 栈 贪心 字符串 单调栈 👍 758 👎 0

package leetcode.editor.cn;

import java.util.Stack;

public class P402RemoveKDigits {
    public static void main(String[] args) {
        Solution solution = new P402RemoveKDigits().new Solution();

        String s = solution.removeKdigits("1432219", 3);

        System.out.println("ans " + s);
        s = solution.removeKdigits("10200", 1);

        System.out.println("ans " + s);

        s = solution.removeKdigits("1223459", 3);

        System.out.println("ans " + s);


        s = solution.removeKdigits("100", 1);

        System.out.println("ans " + s);
        System.out.println("Hello world");
    }
    //leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public String removeKdigits(String num, int k) {

        if (k >= num.length()) {
            return "0";
        }
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < num.length(); i++) {
            if (stack.isEmpty() || k == 0) {
                stack.push(num.charAt(i));
            } else {
                if (num.charAt(i) < stack.peek() ) {
                    while (!stack.empty() && num.charAt(i) < stack.peek() && k > 0) {
                        stack.pop();
                        k--;
                    }
                }
                stack.push(num.charAt(i));
            }
        }
        while (k > 0 ) {
            stack.pop();
            k--;
        }

        StringBuilder sb = new StringBuilder();
        while (!stack.empty()) {
            sb.append(stack.pop());
        }

        String ans = sb.reverse().toString();

        if (ans.isEmpty()) {
            return "0";
        }
        boolean startZero = ans.charAt(0) == '0';
        if (!startZero) {
            return ans;
        }
        for (int i = 1; i < ans.length(); i++) {
            if (ans.charAt(i) != '0') {
                return ans.substring(i);
            }
        }
        return "0";


    }
}
//leetcode submit region end(Prohibit modification and deletion)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值