20200611
难度:中等
根据每日 气温 列表,请重新生成一个列表,对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
单调栈
维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。
class Solution {
public int[] dailyTemperatures(int[] T) {
Deque <Integer> stack = new LinkedList<>();
int[] ans = new int[T.length];
for(int i = 0; i < T.length; i++){
while(!stack.isEmpty() && T[i] > T[stack.peek()]){
int prevIndex = stack.pop();
ans[prevIndex] = i-prevIndex;
}
stack.push(i);
}
return ans;
}
}
Tips:
Java中栈为什么不用Stack,要用Deque?参考这个链接https://mp.weixin.qq.com/s/Ba8jrULf8NJbENK6WGrVWg
Q:在 Java 语言中,不推荐使用 Stack 类?
是的。实际上,这个不推荐不是某个技术专家或者某个企业的规范标准,而是来自 Java 官方。
Q:Java 中的 Stack 类到底怎么了?
Java 中的 Stack 类,最大的问题是,继承了 Vector 这个类。最大的问题在于,继承使得子类继承了父类的所有公有方法。而 Vector 作为动态数组,是有能力在数组中的任何位置添加或者删除元素的。因此,Stack 继承了 Vector,Stack 也有这样的能力!会破坏栈这种数据结构的封装
Q:问题出在哪里?
Stack 和 Vector 之间的关系,不应该是继承关系,而应该是组合关系(composition)。继承关系描述的是 is-a 的关系,即“是一个”的关系。而组合关系描述的是 has-a 的关系,即“有一个”的关系。
Q:为什么使用接口?
接口最大的意义之一,就是做了更高层次的抽象:只定义了一个类应该满足哪些方法,而对具体的实现方式不做限制。Deque 是双端队列的意思。所谓的双端队列,就是能在线性数据结构的两段,进行插入和删除操作。
**其他相似的单调栈题目:**单调栈一般用来处理一种典型的问题,即 Next Greater Element
难度:简单
给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。
示例 2:
输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
提示:
nums1
和nums2
中所有元素是唯一的。nums1
和nums2
的数组大小都不超过1000。
单调栈
这里栈里面放的是值
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Deque<Integer> stack = new LinkedList<>();
Map<Integer, Integer> map = new HashMap<>();
// 对nums2 中的每一个元素,求出其下一个更大的元素
for(int i = 0; i < nums2.length; i++){
while(!stack.isEmpty() && nums2[i] > stack.peek()){
map.put(stack.pop(), nums2[i]);
}
stack.push(nums2[i]);
}
while(!stack.empty()){
map.put(stack.pop(), -1);
}
//在map中找到nums1中每个元素的下一个更大元素
for(int i = 0; i < nums1.length; i++){
nums1[i] = map.get(nums1[i]);
}
return nums1;
}
}
也可以从后往前扫描。排队看后面第一个比自己高的,从后往前遍历,可以很好地避免不知道后面是什么情况。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Deque<Integer> stack = new LinkedList<>();
Map<Integer, Integer> map = new HashMap<>();
int[] ans = new int[nums1.length];
for(int i = nums2.length-1; i >= 0; i--){
while(!stack.isEmpty() && stack.peek() <= nums2[i]){
stack.pop();//个子矮的走开
}
map.put(nums2[i], stack.isEmpty() ? -1 : stack.peek());
stack.push(nums2[i]);
}
for(int i = 0; i < nums1.length; i++){
nums1[i] = map.get(nums1[i]);
}
return nums1;
}
}
难度:中等
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
是循环数组,所以遍历两遍数组,效果等同于循环。同样用单调栈
class Solution {
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
Deque<Integer> stack = new LinkedList<>();
int[] ans = new int[nums.length];
for(int i = 2*n-1; i >= 0; i--){
while(!stack.isEmpty() && stack.peek() <= nums[i % n]){
stack.pop();
}
ans[i % n] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i % n]);
}
return ans;
}
}
由于本题中未规定数组中元素无重复,所以为了唯一识别一个元素,栈中需要保存数组的索引,而不是数组元素的值。(虽然上面的代码也能通过所有测试用例)
class Solution {
public int[] nextGreaterElements(int[] nums) {
int n = nums.length;
Deque<Integer> stack = new LinkedList<>();
int[] ans = new int[nums.length];
for(int i = 2*n-1; i >= 0; i--){
while(!stack.isEmpty() && nums[stack.peek()] <= nums[i % n]){
stack.pop();
}
ans[i % n] = stack.isEmpty() ? -1 : nums[stack.peek()];
stack.push(i % n);
}
return ans;
}
}
难度:困难
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例:
输入: [2,1,5,6,2,3]
输出: 10
单调栈 + 加入哨兵
遍历的时候,记录下标,如果当前的高度比它之前的高度严格小于的时候,就可以直接确定之前的那个高的柱形的最大矩形的面积。
维护一个单调栈,栈中总是保存递增元素的索引,当遇到比栈顶元素小的元素时,将栈顶元素依次出栈,每次都计算栈中的bar能围成的面积,直到栈顶元素小于当前元素就停止出栈。
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
if(n == 0) return 0;
if(n == 1) return heights[0];
int res = 0;
// 放入左右两个哨兵
int[] newHeights = new int[n + 2];
newHeights[0] = 0;
System.arraycopy(heights, 0, newHeights, 1, n);
newHeights[n+1] = 0;
n += 2;
heights = newHeights;
Deque<Integer> stack = new ArrayDeque<>(n);
stack.addLast(0);//放入哨兵
for(int i = 1; i < n; i++){
while(heights[i] < heights[stack.peekLast()]){
int curHeight = heights[stack.pollLast()];
int curWidth = i - stack.peekLast() - 1;
res = Math.max(res, curHeight * curWidth);
}
stack.addLast(i);
}
return res;
}
}
我的公众号: 暂时每日分享LeetCode,我不断学习的过程中,公众号也在不断充实,欢迎大家扫码关注。