leetcode(单调栈专题)

1.导论

  • 什么是单调栈?

就是栈里的元素保持升序或则降序。

  • 什么时候用单调栈

通常是一位数组,要寻找任一个元素的右边或则左边第一个比自己大或则小的元素的位置,此时我们就要想到可以用单调栈了。

  • 使用单边栈主要有三个判断条件

当前元素小于栈顶元素
当前元素等于栈顶元素
当前元素大于栈顶元素

2.编程题

2.1 739. 每日温度

请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
1 <= temperatures.length <= 105
30 <= temperatures[i] <= 100

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] ans = new int[n];
        Deque<Integer> stack = new LinkedList<>();//维护一个递减栈,栈中保存数组下标
        for(int i=0;i<n;++i){
            while(!stack.isEmpty() && temperatures[i]>temperatures[stack.peek()]){
                ans[stack.peek()] = i - stack.pop();
            }
            stack.push(i);
        }
        return ans;
    }
}

2.2 496. 下一个更大元素 I

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
1 <= nums1.length <= nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 104
nums1和nums2中所有整数 互不相同
nums1 中的所有整数同样出现在 nums2 中

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int[] ans = new int[n];
        Arrays.fill(ans,-1);
        Deque<Integer> stack = new LinkedList<>();//维护递减栈,存储数组下标
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<n;++i){
            map.put(nums1[i],i);
        }
        for(int i=0;i<nums2.length;++i){
            while(!stack.isEmpty() && nums2[i]>nums2[stack.peek()]){
                if(map.containsKey(nums2[stack.peek()])){
                    int index = map.get(nums2[stack.peek()]);
                    ans[index] = nums2[i];
                }
                stack.pop();
            }
            stack.push(i);
        }
        return ans;
    }
}

2.3 503. 下一个更大元素 II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
输入数组的长度不会超过 10000。

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        int[] ans = new int[n];
        Arrays.fill(ans,-1);
        Deque<Integer> stack = new LinkedList<>();//维护一个递减栈,保存数组下标
        for(int i=0;i<2*n;++i){
            while(!stack.isEmpty() && nums[i%n]>nums[stack.peek()]){
                ans[stack.pop()] = nums[i%n];
            }
            stack.push(i%n);
        }
        return ans;
    }
}

2.4 42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105

//双指针
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        for(int i=0;i<height.length;++i){
            //第一个柱子和最后一个柱子不接雨水
            if(i == 0 || i == height.length-1) continue;

            int lHeight = height[i];//记录左边柱子的最高高度
            int rHeight = height[i];//记录右边边柱子的最高高度
            for(int l=i-1;l>=0;--l){
                lHeight = Math.max(lHeight,height[l]);
            }
            for(int r=i+1;r<height.length;++r){
                rHeight = Math.max(rHeight,height[r]);
            }
            int h = Math.min(lHeight,rHeight)-height[i];
            if(h > 0) ans += h;
        }
        return ans;
    }    
}
//双指针优化
//如果height[left] < height[right] 则leftMax一定小于rightMax
//同理height[left] >= height[right],则leftMax一定大于等于rightMax
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
}


//动态规划
class Solution {
    public int trap(int[] height) {
        /**
            1.ldp[i]表示height[i]柱子左边柱子的最大高度
                rdp[i]表示height[i]柱子右边边柱子的最大高度
            2.ldp[i] = Math.max(height[i],ldp[i-1]);
                rdp[i] = Math.max(height[i],rdp[i+1]);
            3.ldp[0] = height[0];rdp[n-1] = height[n-1];
            4.求左边顺序,求右边逆序

         */
        int n = height.length;
        if(n<=2) return 0;
        int[] ldp = new int[n];
        int[] rdp = new int[n];

        //记录每个柱子左边柱子的最大高度
        ldp[0] = height[0];
        for(int i = 1; i < n; ++i){
            ldp[i] = Math.max(height[i],ldp[i-1]);
        }
        //记录每个柱子右边柱子的最大高度
        rdp[n-1] = height[n-1];
        for(int i = n-2; i >= 0; --i){
            rdp[i] = Math.max(height[i],rdp[i+1]);
        }
        //求和
        int ans= 0;
        for(int i=0;i<n;++i){
            int h = Math.min(ldp[i],rdp[i])-height[i];
            if(h > 0) ans += h;
        }
        return ans;
    }
}
//单调栈
class Solution {
    public int trap(int[] height) {
        Deque<Integer> stack = new LinkedList<>();//维护递减栈,保存数组下标
        int ans = 0;
        for(int i = 0; i < height.length; ++i){
            while(!stack.isEmpty() && height[i] > height[stack.peek()]){
                int mid = stack.pop();//中间柱子的下标
                if(!stack.isEmpty()){
                    //height[stack.peek()] 相对于中间柱子的左边柱子高度
                    //height[i] 相对于中间柱子的右边柱子高度
                    //height[mid] 中间柱子高度
                    int h = Math.min(height[stack.peek()],height[i])-height[mid];
                    int w = i - stack.peek() -1;
                    ans += h*w;
                }
            }
            stack.push(i);
        }
        return ans;
    }
}

2.5 84. 柱状图中最大的矩形

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

求在该柱状图中,能够勾勒出来的矩形的最大面积。
1 <= heights.length <=105
0 <= heights[i] <= 104

//本题是要找每个柱子左右两边第一个小于该柱子的柱子
class Solution {
    public int largestRectangleArea(int[] heights) {
        int[] temp = new int[heights.length+2];
        System.arraycopy(heights,0,temp,1,heights.length);
        Deque<Integer> stack = new LinkedList<>();
        int ans = 0;
        for(int i = 0; i < temp.length; ++i){
            while(!stack.isEmpty() && temp[i] < temp[stack.peek()]){
                int mid = stack.pop();
                int h = temp[mid];
                int w = i-stack.peek()-1;
                ans = Math.max(ans,w*h);
            }
            stack.push(i);
        }
        return ans;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
引用\[1\]提供了一个朴素的解法,使用两个来存储字符串,一个用来存储普通字符,另一个用来存储特殊字符。遍历字符串,如果是普通字符则压入第一个,如果是特殊字符则弹出第一个顶元素。最后比较两个是否相同即可判断字符串是否有效。这个解法的时间复杂度是O(M + N),空间复杂度也是O(M + N)。\[1\] 引用\[2\]提供了另一个的应用场景,即判断括号是否有效。遍历字符串,如果是左括号则压入,如果是右括号则判断和顶元素是否匹配,不匹配则返回false,匹配则弹出顶元素。最后判断是否为空即可判断括号是否有效。\[2\] 引用\[3\]也提供了一个判断括号是否有效的解法,使用来操作。遍历字符串,如果是左括号则压入,如果是右括号则判断和顶元素是否匹配,不匹配则返回false,匹配则弹出顶元素。最后判断是否为空即可判断括号是否有效。这个解法使用了HashMap来存储括号的对应关系。\[3\] 综上所述,在解决字符串相关问题中有着广泛的应用,包括判断字符串是否有效、逆波兰表达式等。在解决这些问题时,可以帮助我们保存和处理字符的顺序,从而简化问题的处理过程。 #### 引用[.reference_title] - *1* *3* [Leetcode刷题03-](https://blog.csdn.net/weixin_47802917/article/details/123007699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* [leetCode-类型详解](https://blog.csdn.net/zhiyikeji/article/details/125508011)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Listen·Rain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值