java单调栈算法实例

3 篇文章 0 订阅
1 篇文章 0 订阅
文章介绍了单调栈数据结构在解决数组问题中的应用,包括求解每日温度中第一个大于目标温度的天数、寻找数组中下一个更大元素、求解循环数组的下一个最大元素以及计算接雨水问题和最大矩形面积问题的解决方案。通过多个示例展示了单调栈如何帮助优化算法效率。
摘要由CSDN通过智能技术生成

1 理论基础

  • 单调栈适用于求解数组中某个元素左边或者右边比它小或者大的的第一个元素的位置
  • 单调栈的含义就是一个栈里面元素是单调递增或者单调递减的栈,一般存放数组的下标

2. 每日温度

求解数组中第一个大于target的元素距离target的距离

//单调栈
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
       int[] res = new int[temperatures.length];
       Stack<Integer> stack = new Stack<>();
       stack.push(0);
       for(int i = 1; i < temperatures.length; i++) {
           //栈顶下标对应的数组元素小于等于当前下标的数组元素,元素下标入栈
           if(temperatures[stack.peek()] >= temperatures[i]) {
               stack.push(i);
           }
           //大于的时候就获取栈顶当前数组下标和栈顶元素下标的差值
           else{
               while(!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
                    res[stack.peek()] = i - stack.peek();
                    stack.pop(); 
               }
               stack.push(i);         //这里将元素拿出来了之后需要加入下一个元素
           }
       }
       return res;
    }
}

3. 下一个更大元素

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        //先做一个map映射,以便于在nums2中寻找nums1
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums1.length; i++) {
            map.put(nums1[i], i);
        }
        int len = nums1.length;
        int[] res = new int[len];   //用于存储最终结果
        Arrays.fill(res, -1);       //初始化为-1
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        /****************
        先入栈,下一个比栈顶下标对应的nums2元素小,就入栈,
        比栈顶小标对应的元素大就找到栈顶下标对应的nums1的
        索引,将当前大的元素赋值给找到的索引下标数组
        *****************/
        for(int i = 1; i < nums2.length; i++) {
            if(nums2[i] <= nums2[stack.peek()]) {
                stack.push(i);
            }
            else{
                while(!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
                    //map.get():根据key访问value
                    if(map.containsKey(nums2[stack.peek()])) {
                        int key = map.get(nums2[stack.peek()]);
                        res[key] = nums2[i]; 
                    }
                    stack.pop();      
                }
                stack.push(i);
            }
        }
        return res;
    }
}

4. 求解循环数组的下一个最大元素

//先将数组复制一遍,组成一个为原来两倍的新数组
//然后利用这个新数组,使用单调栈寻找后面比它大
//的元素,不过时间复杂度明显较高
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int len = nums.length;
        int[] copyNums = new int[2 * nums.length];
        for(int i = 0; i < copyNums.length; i++) {
            if(i < len) {
                copyNums[i] = nums[i];
            }
            else{
                copyNums[i] = nums[i - len];
            }
        }
        
        int[] temp = new int[2*len];
        Arrays.fill(temp, -1);
        int[] res = new int[len];
        Arrays.fill(res, -1);
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        for(int i = 1; i < copyNums.length; i++) {
            if(copyNums[i] <= copyNums[stack.peek()]) {
                stack.push(i);
            }
            else{
                while(!stack.isEmpty() && copyNums[i] > copyNums[stack.peek()]) {
                    temp[stack.peek()] = copyNums[i];
                    stack.pop();
                }
                stack.push(i);
            }
        }

        for(int i = 0; i < len ; i++) {
            res[i] = temp[i];
        }
        return res;
    }
}

//简化方法
class Solution {
    public int[] nextGreaterElements(int[] nums) {
        if(nums == null || nums.length <= 1) {
            return new int[]{-1};
        }
        Stack<Integer> stack = new Stack<>();
        int len = nums.length;
        int[] res = new int[len];
        Arrays.fill(res, -1);
        for(int i = 0; i < 2 * len; i++) {
            while(!stack.isEmpty() && nums[i % len] > nums[stack.peek()]) {
                res[stack.peek()] = nums[i % len];         //处理环的问题
                stack.pop();
            }
            stack.push(i % len);
        }
        return res;
    }
}

5. 接雨水 :面试常考


//解题关键在于栈顶元素下一个是它左边比它大的一个元素
//当前遍历到的比栈顶元素大的话它就是其右边比它大的第一个元素
class Solution {
    public int trap(int[] height) {
        if(height == null || height.length == 0) {
            return 0;
        }
        int waterSquare = 0;
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        for(int i = 1; i < height.length; i++) {
            if(height[stack.peek()] >= height[i]) {
                stack.push(i);
            }
            else{
                while(!stack.isEmpty() && height[stack.peek()] < height[i]) {
                    int mid = stack.pop();    
                    if(!stack.isEmpty()) {
                        int h = Math.min(height[i], height[stack.peek()]) - height[mid];
                        int w = i - stack.peek() - 1;
                        waterSquare += h * w;
                    }  
                }
                stack.push(i);
            }
        }
        return waterSquare;
    }
}
  1. 寻找柱状图中构成的最大矩形
//暴力解法: 超时
class Solution {
    public int largestRectangleArea(int[] heights) {
        int sum = 0;
        for(int i = 0; i < heights.length; i++) {
            int left = i;
            int right = i;
            //往左往右比较找到比当前大的值
            for( ;left >= 0; left--) {
                if(heights[left] < heights[i]) break;
            }
            for( ; right < heights.length; right++) {
                if(heights[right] < heights[i]) break;
            }

            int w = right - left - 1;
            int h = heights[i];
            sum = Math.max(sum, w * h);
        }
        return sum;
    }
}

//单调栈

```java
//单调栈
class Solution {
    public int largestRectangleArea(int[] heights) {
        if(heights == null && heights.length == 0) {
            return 0;
        }
        //给数组尾加一个0,防止单调递减的情况永远不弹出
        //给数组头加一个0,防止只有两个元素的时候不够处理
        int len = heights.length;
        int[] newHeights = new int[len + 2];
        newHeights[0] = 0;
        newHeights[len + 1] = 0;
        for(int i = 1; i < len + 1; i++) {
            newHeights[i] = heights[i - 1];
        }
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        int maxSquare = 0;
        for(int i = 1; i < newHeights.length; i++) {
            if(newHeights[i] >= newHeights[stack.peek()]) {
                stack.push(i);
            }
            else{
                while(!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
                    int mid = stack.pop();
                    if(!stack.isEmpty()) {
                        int h = newHeights[mid];
                        int w = i - stack.peek() - 1;
                        maxSquare = Math.max(maxSquare, h * w);
                    }
                }
                stack.push(i);
            }
        }
        return maxSquare;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值