单调栈
单调递增栈:栈底到栈顶递增 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 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。