代码随想录算法训练营day63 || 53. 下一个更大元素,42. 接雨水

单调栈,成环了可怎么办?LeetCode:503.下一个更大元素II_哔哩哔哩_bilibili

单调栈,经典来袭!LeetCode:42.接雨水_哔哩哔哩_bilibili

53. 下一个更大元素

// 时间复杂度O(2n)~O(n^2)
// 空间复杂度O(2n)

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        // 直接以单调栈的思想进行求解
        int n = nums.length;
        int[] ans = new int[n];
        // Stack<Integer> stack = new Stack<>();
        ArrayDeque<Integer> stack = new ArrayDeque<>();
        
        // 栈内按照递增的顺序进入元素,越是考后的元素,其值越大,注意次数从后往前遍历时,需要向左找比当前栈顶小的元素入栈,如果出现与栈顶相等的,则需要弹出栈顶,否则nums[i]无法真正找到比自己大的元素,就会停留在和自己一样大小的元素了
        for(int i=2*n-1; i>=0; i--){
            while(stack.size()>0 && nums[i%n] >= nums[stack.peek()])
                stack.pop();
            if(i<n)
                ans[i] = stack.size() == 0 ? -1: nums[stack.peek()];

            stack.push(i%n);
        } 
        // 采用从左至右的遍历方式进行遍历,上种方法效率更好
        // Arrays.fill(ans, -1);
        // stack.push(0);
        // for(int i=1; i<2*n; i++){
        //     // 此方式,ans的更新是在每一次弹出一个栈内元素时进行,因此从左到右遍历,出栈操作说明碰到了大于栈顶的右侧第一个元素,注意此种情况下是大于,而不是大于等于了
        //     while(!stack.isEmpty() && nums[i%n] > nums[stack.peek()]){
        //         int idx = stack.pop();
        //         ans[idx] = nums[i%n];
        //     }
        //     stack.push(i%n);
        // }  
    
        return ans;   
    }
}

42. 接雨水

思路:读题明确需要双指针来描述一个长方形,里面可以填入雨水。因此首先使用双指针进行求解;双指针是同侧快慢指针,两个分别维护长方形的一条宽。接下来思考双指针是如何移动的?快指针随着数组遍历而移动,慢指针总是指向height不为零的位置,那么如何在快指针访问的位置适时的赋予慢指针呢?观察求和填雨水的求法,我们需要比较快慢指针所指向位置的高度,选择较小的来计算。因此得出慢指针的更新应当在遇到更高的高度后,指向其。即指向右侧第一个大于当前位置的元素,与单调栈关联。

求解时,采用了从左至右的遍历思路,则在每一次出栈都需要对结果进行更新。单调栈从栈首至栈尾是递增,所以如果出栈的情况,那么一定是可以有雨水可接。但是需要注意栈中存在32101这样的山谷状,此时出栈一次求取的是最山谷部分的雨水量,其他部分可以随着后续出栈操作而更新,但是需要需要减去先前已出栈部分的 高度差differ。特别注意当存在高度差后,当前高度没有把栈内元素完全清空,且栈内剩余元素是大于当前高度的,则此时应该先将栈顶元素与当前位置之间的雨水量求出来,否则就会出现有一块面积遗漏。举个例子4,2,1,3,0,5,如果不额外计算一步的话,橙色块的雨水就没有能够记录下来。

本题的单调栈的规则是这样:

  • 大于栈顶元素(都行,因为设置了高度差这个量),则出栈栈顶元素,开始计算栈顶元素下标与当前元素下标之间的雨水量;
  • 小于栈顶元素,直接入栈;
// 时间复杂度O(n) ~ O(n^2)
// 空间复杂度O(1)

class Solution {
    public int trap(int[] height) {
        // 优先双指针(同侧快慢双指针),时间复杂度比较高,可能会超时
        int n = height.length;
        int amount = 0;
        int differ = 0;
        ArrayDeque<Integer> stack = new ArrayDeque<>();

        // 从左向右遍历形成单调栈
        // 初始化
        for(int i=0; i<n; i++){
            if(height[i] > 0){
                stack.push(i);
                break;
            }
        } 
        if(stack.size() == 0)
            return 0;
        
        for(int j=stack.peek()+1; j<n; j++){
            int am = 0;
            boolean flag = false;
            while(!stack.isEmpty() && height[j] > height[stack.peek()]){
                // idx一定是比当前j位置的元素来的小的
                flag = true;
                int idx = stack.pop();
                // if(height[idx] == 0)
                    // zero++;
                if(height[idx] > 0){
                    // 碰到栈内当前最小的高度,与nums[j]之间只会存在0高度
                    am += (height[idx]-differ)*(j-idx-1);
                    differ = height[idx];
                }   
            }
            // 此时要么栈内已经为空,要么height[j]遇到了一个更大的高度,那么在入栈前,需要将这部分高度差内可以填入的水率先收集起来
            if(flag && !stack.isEmpty())
                if(height[j] > differ)
                    am += (height[j]-differ)*(j-stack.peek()-1);
            
            stack.push(j);
            differ = 0;
            amount += am;
        }

        return amount;
    }
}

其他方法后序再补充,上面的方法是目前先想的一种单调栈解法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值