单调栈
- 通常在一维数组中,要找任何一个元素的右边或左边第一个比自己大或小的元素的位置,此时可以使用单调栈。
- 单调栈的本质:空间换时间,在遍历过程中用一个栈来记录遍历过的元素。
- 使用单调栈需明确以下问题:
-
① 单调栈里存放的是什么?
单调栈里存放一维数组中元素的下标。使用对应的元素时,直接用一维数组[i] 获取即可。 -
② 单调栈中的元素是递增?还是递减?
这里说的顺序是:栈头到栈底的顺序。栈内元素递增顺序:求一个元素右边的第一个更大的元素时,单调栈是递增的。此时,往栈中加的新元素比栈顶元素小。加入了比栈头大的元素时要把栈头的大的元素都弹出。
栈内元素递减顺序:求一个元素右边的第一个更小的元素时,单调栈是递减的,往栈中加的元素比栈顶元素大。此时,往栈里加入的元素比栈顶大就不用弹出,比栈头元素小时就要把栈头弹出。
-
- 单调栈主要的三个判断条件
- ① 当前遍历的元素小于栈顶元素
- ② 当前遍历的元素等于栈顶元素
- ③ 当前遍历的元素大于栈顶元素
739.每日温度
-
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1: 输入: temperatures = [73,74,75,71,69,72,76,73] 输出: [1,1,4,2,1,1,0,0] 示例 2: 输入: temperatures = [30,40,50,60] 输出: [1,1,1,0] 示例 3: 输入: temperatures = [30,60,90] 输出: [1,1,0]
-
单调栈
- 通常一维数组,要寻找一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时就可以使用单调栈。
- 本质:空间换时间。在遍历过程中用一个栈来记录右边第一个元素比当前元素高的元素,优点是整个数组只需要遍历一次。
- 看随想录讲解
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
//记录每个元素的距离下一个更高温度出现的距离
int[] res = new int[temperatures.length];
//单调栈中加入的是遍历元素的下标,因为要计算距离
Deque<Integer> stack = new LinkedList<>();
//先往栈里放一个0(第一个元素的下标0)
stack.push(0);
for(int i = 0; i < temperatures.length; i++) {
//若进来的元素不比当前栈里的顶端元素大,直接把元素下标放进栈里
if(temperatures[i] <= temperatures[stack.peek()]) {
stack.push(i);
} else {
//如果加入的元素比当前栈顶大,得到一个对应栈顶元素的res,记录后把栈顶元素弹出去。如果加入的元素还比下一个栈顶大,就继续记录。直到有可能把栈都弹空了就结束。最后在把这个大的元素放进栈里。
while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){
//res[stack中存的是下标,对应原数组下标]存当前大于栈顶元素的下标与当前栈顶元素下标的差值
res[stack.peek()] = i - stack.peek();
//然后把栈顶元素弹出
stack.pop();
}
//后面继续放入新的元素
}
stack.push(i);
}
return res;
}
}
496.下一个更大元素Ⅰ
-
题意:nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
示例 1: 输入:nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出:[-1,3,-1] 解释:nums1 中每个值的下一个更大元素如下所述: - 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。 - 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。 - 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。 示例 2: 输入:nums1 = [2,4], nums2 = [1,2,3,4]. 输出:[3,-1] 解释:nums1 中每个值的下一个更大元素如下所述: - 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。 - 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。
-
思路:nums1是nums2的子集。根据nums2判断当前元素的下一个更大的元素是否出现,看当前元素是否存在在nums1中。如果存在,用value接收nums1的索引用于对应res的索引,以返回结果,结果就是当前nums[2]的元素。
-
栈用上一题的Deque也可以。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] res = new int[nums1.length];
//或者写 Arrays.fill(res, -1);
//结果数组初始化为-1,因为没有找到更大元素的话返回-1
for(int i = 0; i < res.length; i++) {
res[i] = -1;
}
//创建单调栈
Stack<Integer> temp = new Stack<>();
//创建一个映射HashMap来存储nums1的索引和对应的值。
//因为题中说,nums1中是没有重复元素的数组,所以可以使用map来做映射。Key存数组值,Value存数组下标
HashMap<Integer, Integer> hashMap = new HashMap<>();
for(int i = 0; i < nums1.length; i++) {
hashMap.put(nums1[i], i);
}
temp.add(0);
//遍历nums2,判断后面的值是否比当前值大
for(int i = 0; i < nums2.length; i++) {
if(nums2[i] <= nums2[temp.peek()]) {
temp.add(i);
} else {
while(!temp.isEmpty() &&
nums2[i] > nums2[temp.peek()]) {
//栈中放的是nums2元素的索引值
//判断此时的栈顶值对应的nums2的元素是否在nums1中出现过,找对应的Key
if(hashMap.containsKey(nums2[temp.peek()])) {
//记录Key所对应的Value,即nums1对应元素的下标
Integer index = hashMap.get(nums2[temp.peek()]);
//结果数组中,下标对应的结果就是找到的此时大于栈顶元素的nums2中的元素
res[index] = nums2[i];
}
//弹出栈顶元素
temp.pop();
}
temp.add(i);
}
}
return res;
}
}