代码随想录算法训练营第五十九天-单调栈2| 503.下一个更大元素II 42. 接雨水

503. Next Greater Element II

成环就用取模mod方法

import java.util.Arrays;
import java.util.Stack;

public class NextGreaterElement2 {
    public int[] nextGreaterElements(int[] nums) {
        //边界判断
        if(nums == null || nums.length <= 1){
            return new int[]{-1};
        }
        int size = nums.length;
        int[] result = new int[size];//存放结果
        Arrays.fill(result,-1);//默认全部初始化为-1
        Stack<Integer> st = new Stack<>();//栈中存放的是nums中的元素下标
        for(int i = 0; i < 2* size; i++){
            while(!st.isEmpty()&& nums[ i % size] > nums[st.peek()]){
                result[st.peek()] = nums[ i % size];//更新result
                st.pop();//弹出栈顶
            }
            st.push(i % size);
        }
        return result;

    }
}

42. Trapping Rain Water 接雨水

接雨水这道题目是 面试中特别高频的一道题,也是单调栈 应用的题目,大家好好做做。

建议是掌握 双指针 和单调栈,因为在面试中 写出单调栈可能 有点难度,但双指针思路更直接一些。

在时间紧张的情况有,能写出双指针法也是不错的,然后可以和面试官在慢慢讨论如何优化。

暴力解法

本题暴力解法也是也是使用双指针。

首先要明确,要按照行来计算,还是按照列来计算。

首先,如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。

可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。

列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。

列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。

列4 柱子的高度为1(以下用height表示)

那么列4的雨水高度为 列3和列7的高度最小值减列4高度,即: min(lHeight, rHeight) - height。

列4的雨水高度求出来了,宽度为1,相乘就是列4的雨水体积了。

此时求出了列4的雨水体积。

一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。

 首先从头遍历所有的列,并且要注意第一个柱子和最后一个柱子不接雨水。

public class TrappingRainWater {

    //暴力解法
    public int trap(int[] height) {
        int sum = 0;
        for(int i = 0; i < height.length; i++){
            // 第一个柱子和最后一个柱子不接雨水
            if(i == 0|| i == height.length -1) continue;

            int rHeight = height[i];// 记录右边柱子的最高高度
            int lHeight = height[i];// 记录左边柱子的最高高度
            for(int r = i + 1; i < height.length; i++){
                if(height[r] > rHeight) rHeight = height[r];
            }
            for(int l = i - 1; l >=0; l--){
                if(height[l] > lHeight) lHeight = height[l];
            }
            int h = Math.min(lHeight, rHeight) - height[i];
            if(h > 0) sum +=h;
        }
        return sum;

    }
}

单调栈解法

关于单调栈的理论基础,单调栈适合解决什么问题,单调栈的工作过程,大家可以先看这题讲解 739. 每日温度 (opens new window)

单调栈就是保持栈内元素有序。和栈与队列:单调队列 (opens new window)一样,需要我们自己维持顺序,没有现成的容器可以用。

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

而接雨水这道题目,我们正需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积。

2. 使用单调栈内元素的顺序 

从大到小还是从小到大呢?

从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。

因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。

关于单调栈的顺序给大家一个总结: 739. 每日温度 (opens new window)中求一个元素右边第一个更大元素,单调栈就是递增的,84.柱状图中最大的矩形 (opens new window)求一个元素右边第一个更小元素,单调栈就是递减的。

3. 遇到相同高度的柱子怎么办。

遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。

例如 5 5 1 3 这种情况。如果添加第二个5的时候就应该将第一个5的下标弹出,把第二个5添加到栈中。

因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度

4. 栈里要保存什么数值

使用单调栈,也是通过 长 * 宽 来计算雨水面积的。

长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,

那么栈里有没有必要存一个pair<int, int>类型的元素,保存柱子的高度和下标呢。

其实不用,栈里就存放下标就行,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。

public int trap(int[] height) {

        //单调栈-横向

        int size = height.length;
        if(size <= 2) return 0;
        // in the stack, we push the index of array
        // using height[] to access the real height
        Stack<Integer> stack = new Stack<>();
        stack.push(0);


        int sum = 0;
        for(int index = 1; index < size; index++){
            int stackTop =stack.peek();
            if(height[index] < height[stackTop]){
                stack.push(index);
            }else if(height[index] == height[stackTop]){
                // 因为相等的相邻墙,左边一个是不可能存放雨水的,所以pop左边的index, push当前的index
                stack.pop();
                stack.push(index);
            }else{
                //pop up all lower value
                int heightAtIdx = height[index];
                while (!stack.isEmpty() && (heightAtIdx > height[stackTop])){
                    int mid = stack.pop();

                    if(!stack.isEmpty()){
                        int left = stack.peek();

                        int h = Math.min(height[left], height[index]) - height[mid];
                        int w = index - left - 1;
                        int hold = h * w;
                        if(hold > 0) sum += hold;
                        stackTop = stack.peek();
                    }
                }
                stack.push(index);
            }
        }
        return sum;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值