【leetcode】单调栈

1,299 阅读4分钟

单调栈

单调递增栈:栈底到栈顶递增 1,2,3,4。 单调递减栈:栈底到栈顶递减 4,3,2,1。

栈中元素具有单调性。让要入栈的元素影响整体的单调性时,需出栈一些元素以维持单调性。 出栈时,即可得到出栈元素左边/右边第一个比出栈元素大/小的元素。

求解数组中元素右边第一个比它小的元素的下标,从前往后,构造单调递增栈; 求解数组中元素右边第一个比它大的元素的下标,从前往后,构造单调递减栈; 求解数组中元素左边第一个比它小的元素的下标,从后往前,构造单调递减栈; 求解数组中元素左边第一个比它小的元素的下标,从后往前,构造单调递增栈。

[496] 下一个更大元素 I

题目:给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。 nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

主要是求nums2的“下一个更大元素”数组,存入map,遍历nums1从map获取。 构造单调递减栈,遇到大于栈顶元素的元素时,栈顶元素即找到了它自己的下一个更大元素。重复此操作,直到栈空或不大于栈顶元素。

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int n = nums2.length;
        Map<Integer, Integer> map = new HashMap<>();
        Deque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && nums2[i] > stack.peekFirst()) {
                map.put(stack.pollFirst(), nums2[i]);
            }
            stack.offerFirst(nums2[i]);
        }
        int[] res = new int[nums1.length];
        for (int j = 0; j < res.length; j++) {
            res[j] = map.getOrDefault(nums1[j], -1);
        }
        return res;
    }
}

[503] 下一个更大元素 II

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

与“下一个更大元素 I”的区别是数组为循环数组,可以将数组x2拼接起来,当然不用new一个新数组来拼接,只需要通过取余操作遍历2遍即可。 栈中存放元素在“拼接数组”中的下标,获取下表对应的元素值时做取余操作。最后截取数组前一半返回。

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int n = nums.length;
        int[] t = new int[2 * n];
        Arrays.fill(t, -1);
        Deque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < (n * 2); i++) {
            int k = i % n;
            while (!stack.isEmpty() && nums[k] > nums[stack.peekFirst() % n]) {
                t[stack.pollFirst()] = nums[k];
            }
            stack.offerFirst(i);
        }
        return Arrays.copyOfRange(t, 0, n);
    }
}

[739] 每日温度

题目:请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

题目要求在数组中寻找每个元素与其下一个更大元素的索引值之差,例如T=[73, 74, 75, 71, 69, 72, 76, 73], 输出应为res=[1, 1, 4, 2, 1, 1, 0, 0]。 找不到T[i]的下一个更大元素时,res[i]=0,由于new出来的数组每个元素默认为0,因此未出过栈的元素不需要特殊处理。

找下一个更大元素,使用单调递减栈,栈中存放元素索引。遍历数组T,每个元素T[i]与栈顶元素k比较,

  • 若T[i]大于k,k出栈,T[i]就是k的下一个更大的元素,可计算出索引差。之后此操作直到T[i]不大于k,将T[i]入栈;
  • 若T[i]等于或小于k,T[i]入栈即可。
class Solution {
    public int[] dailyTemperatures(int[] T) {
        int n = T.length;
        int[] t = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && T[i] > T[stack.peekFirst()]) {
                int k = stack.pollFirst();
                t[k] = i - k;
                System.out.println("t[" + k + "]=" + (i - k));
            }
            stack.offerFirst(i);
        }
        return t;
    }
}

[1019] 链表中下一个更大节点

题目:求链表中每个节点的下一个更大值,不存在时等于0。

先把链表转为List,再构造单调递减栈。

class Solution {
    public int[] nextLargerNodes(ListNode head) {
        List<Integer> list = new ArrayList<>();
        while (head != null) {
            list.add(head.val);
            head = head.next;
        }

        int[] res = new int[list.size()];
        Deque<Integer> stack = new ArrayDeque<>();
        for (int j = 0; j < list.size(); j++) {
            while (!stack.isEmpty() && list.get(j) > list.get(stack.peekFirst())) {
                res[stack.pollFirst()] = list.get(j);
            }
            stack.offerFirst(j);
        }
        return res;
    }
}

[84] 柱状图中最大的矩形

题目:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        // 1. 找出i左右边界
        int[] left = new int[n];
        int[] right = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();

        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && heights[i] <= heights[stack.peekFirst()]) {
                stack.pollFirst();
            }
            left[i] = stack.isEmpty() ? -1 : stack.peekFirst();
            stack.offerFirst(i);
        }
        stack.clear();
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && heights[i] <= heights[stack.peekFirst()]) {
                stack.pollFirst();
            }
            right[i] = stack.isEmpty() ? n : stack.peekFirst();
            stack.offerFirst(i);
        }
        System.out.println(Arrays.toString(left));
        System.out.println(Arrays.toString(right));

        // 2. 计算每个矩形的值
        int res = 0;
        for (int i = 0; i < n; i++) {
            res = Math.max(res, (right[i] - left[i] - 1) * heights[i]);
        }
        return res;
    }
}

[42] 接雨水

题目:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。