数据结构:单调栈

单调栈核心思想

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

2、单调栈的本质是空间交换时间,栈中记录着之前的元素。

3、需要考虑单调栈中存放什么元素

4、单调栈的三个判断条件

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

下一个元素更大I

力扣题目链接

class Solution {
    // public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    //     int[] result = new int[nums1.length];
    //     Arrays.fill(result, -1);
    //     int idx = 0;
    //     Map<Integer, Integer> map = new HashMap<>();
    //     for (int num : nums2) {
    //         map.put(num, idx++);
    //     }
    //     for (int i = 0; i < nums1.length; ++i) {
    //         for (int j = map.get(nums1[i]) + 1; j < nums2.length; ++j) {
    //             if (nums2[j] > nums1[i]) {
    //                 result[i] = nums2[j];
    //                 break;
    //             }
    //         }
    //     }
    //     return result;
    // }

    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] result = new int[nums1.length];
        Arrays.fill(result, -1);
        Map<Integer, Integer> map = new HashMap<>();
        int idx = 0;
        for (int num : nums1) {
            map.put(num, idx++);
        }
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        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()]) {
                    if (map.containsKey(nums2[stack.peek()])) {
                        result[map.get(nums2[stack.peek()])] = nums2[i];
                    }
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return result;
    }
}

写了两种方法,第一种是暴力方法,第二种是单调栈的方法,栈的顺序是递增顺序。即栈头最小

下一个更大元素II

力扣题目链接

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int[] result = new int[nums.length];
        Arrays.fill(result, -1);
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < nums.length * 2; ++i) {
            while (!stack.isEmpty() && nums[i % nums.length] > nums[stack.peek()]) {
                result[stack.peek()] = nums[i % nums.length];
                stack.pop();
            }
            stack.push(i % nums.length);
        }
        return result;
    }
}

很巧妙的一个用法,就是循环遍历的时候我们可以遍历二倍的长度,数据索引通过当前遍历的索引除以数组长度取余

每日温度

力扣题目链接

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] answer = new int[n];
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        for (int i = 1; i < n; ++i) {
            if (temperatures[i] <= temperatures[stack.peek()]) {
                stack.push(i);
            }
            else {
                while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                    answer[stack.peek()] = i - stack.peek();
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return answer;
    }
}

接雨水

力扣题目链接

class Solution {
    public int trap(int[] height) {
        int size = height.length;
        if (size <= 2) {
            return 0;
        }
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        int sum = 0;
        for (int i = 1; i < size; ++i) {
            if (height[i] < height[stack.peek()]) {
                stack.push(i);
            }
            else if (height[i] == height[stack.peek()]) {
                stack.pop();
                stack.push(i);
            }
            else {
                while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                    int mid = stack.pop();                    
                    if (!stack.isEmpty()) {
                        int left = stack.peek();
                        int height_slot = Math.min(height[left], height[i]) - height[mid];
                        int width = i - left - 1;
                        int area = height_slot * width;
                        if (area > 0) {
                            sum += area;
                        }
                    }
                }
                stack.push(i);
            }
        }
        return sum;
    }
}

单调栈递增顺序(栈头元素最小),分别考虑三个判断条件

柱状图中最大的矩形

力扣题目链接

class Solution {
    public int largestRectangleArea(int[] heights) {
        int[] heights_new = new int[heights.length + 2];
        heights_new[0] = 0;
        heights_new[heights_new.length - 1] = 0;
        for (int index = 0; index < heights.length; ++index) {
            heights_new[index + 1] = heights[index];
        }
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        int result = 0;
        for (int i = 1; i < heights_new.length; ++i) {
            if (heights_new[i] > heights_new[stack.peek()]) {
                stack.push(i);
            }
            else if (heights_new[i] == heights_new[stack.peek()]) {
                stack.pop();
                stack.push(i);
            }
            else {
                while (!stack.isEmpty() && heights_new[i] < heights_new[stack.peek()]) {
                    int mid = stack.pop();
                    if (!stack.isEmpty()) {
                        int left = stack.peek();
                        int width = i - left - 1;
                        int height_slot = heights_new[mid];
                        result = Math.max(result, width * height_slot);
                    }
                }
                stack.push(i);
            }
        }
        return result;
    }
}

单调栈,递减的顺序(栈头最大),分别考虑三个判断条件。这题与接雨水是正好相反的两道题,可以对比参考一下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值