单调栈的特点:
1.别看又是for又是while的,复杂度是o(n)
2.解决四种问题 左边比我 大的/小的 第一个元素 以及 右边比我 小的/大的 第一个元素
记住一定是左边或者右边的 第一个 ,第一个,第一个。重要的事情说三遍。
题目:739 496 503,42,84
接下来一个一个搞起。
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]
我们用这一个题目讲下这玩意怎么用
首先这摆明了要求一个数字右边最大的第一个值,ok上单调栈。之前我们先定义点东西:
int[] res = new int[temperatures.length]; //返回值
Deque<Integer> queue = new LinkedList<Integer>();//首先得有个栈嘛,我这写的是queue,因为用的deque
然后该遍历了。从前往后还是从后往前?从后面往前面好点,因为我们要求的是右边最大,而且是第一个。从右边遍历回来就可以把数组收了。
queue里面存的是我当前元素的后面,记住这个设定。
int[] res = new int[temperatures.length];
Deque<Integer> queue = new LinkedList<Integer>();
for (int i=temperatures.length-1; i>=0 ; i--) {
//我发现我while经常写成if,意思是什么呢?就是如果我当前元素的后面还比我小,那就去了吧
while(!queue.isEmpty() &&
temperatures[queue.peek()] <= temperatures[i]){
queue.pop();,
比如下面这个图,cur是第一个绿条条,值是2,后面的一个棕色条条是1,还跟着一个更小的棕色条条,
那如果我们把棕色条条pop掉,三个绿色的是不是就是单调递增,然后cur的后面那个不就是第一个比它大的了吗(也就是peek元素),如下图
接着怎么办?收res。然后别忘记把cur也收入到queue里面
res[i] = queue.isEmpty()?0:queue.peek()-i;//记得减去i
queue.push(i);
}
return res;
完整代码:
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] res = new int[temperatures.length];
Deque<Integer> queue = new LinkedList<Integer>();
for (int i=temperatures.length-1; i>=0 ; i--) {
//我发现我while经常写成if
while(!queue.isEmpty() &&
temperatures[queue.peek()] <= temperatures[i]){
queue.pop();
}
res[i] = queue.isEmpty()?0:queue.peek()-i;//记得减去i
queue.push(i);
}
return res;
}
}
496.下一个更大的元素
给你两个 没有重复元素 的数组 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 。
提示:
- 1 <= nums1.length <= nums2.length <= 1000
- 0 <= nums1[i], nums2[i] <= 10^4
- nums1和nums2中所有整数 互不相同
- nums1 中的所有整数同样出现在 nums2 中
我们瞅瞅啊,和上一个题目好像差不多的。
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] res = new int[nums1.length];
for(int j=0;j<nums1.length;j++){
int indexInNum2 =-1;
//stack放在里面,防止上一次stack没清理干净。
Deque<Integer> stack = new LinkedList<Integer>();
for(int i=nums2.length-1;i>=0;i--) {
if(nums1[j]==nums2[i]){
indexInNum2=i;
break;
}
}
System.out.println(nums2.length-1+" "+indexInNum2);
for(int i=nums2.length-1;i>=indexInNum2;i--){
int realIndex=i;
while(!stack.isEmpty()
&& stack.peek()<=nums2[realIndex]){
stack.pop();
}
res[j] = stack.isEmpty() ?-1:stack.peek();
stack.push(nums2[realIndex]);
}}
return res;
}
}
503
和上一道题差不多,但是它变成环了。
变成环有一种方法就是把一个一模一样的数组给它从后面接上
那么遍历的时候就要取模
首先我想想哦
单个的时候倒着遍历好像是从
i=nums.length-1开始的
那么i=(nums.length-1)x2就遍历对吗
对你个头!为什么要减一是因为从0开始
所以应该是2xnums.length-1
然后具体到某一个取个模就可以了
然后要注意,push进去的不是下标了哈!
这次是值
代码:
我们先看84题,直方图里面最大那个矩形
这道题我们以每一个矩形的视角来看待
如果我是2,以我的高度能构造的最大好像就我自己
然后我是1,我应该可以一直延伸,如下图:
那么对于一个矩形条来说,它能构造的最大矩形,是由它左边右边各自的[第一个]小于他的值
决定的。
比如上面的5,受限于它左边第一个小过他研也就是1,右边第一个小过它也就是2,所以只能构造5和6两条,最大就是10。
我们上面维护过右边第一个大的数组,那么这次维护两个数组
left和right
left[i]代表在i位置,左边第一个比nums[i]小的值离i的距离。
right[i]代表在i位置,右边第一个比nums[i]小的值离i的距离。
然后注意一下边界,最左边下标是-1,最右边是nums.length
如果最左边没有比nums[i]小的,默认-1处比它小,那么push的距离应该是nums[i]减去-1,也就是nums[i+1]
同理最右边是nums.length-i
上代码
class Solution {
public int largestRectangleArea(int[] heights) {
Deque<Integer> stack = new
LinkedList<Integer>();
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
//求左边第一个比自己小的的距离,求左边那么正着遍历
for(int i=0;i<n;i++){
while(!stack.isEmpty() && heights[stack.peek()]>=heights[i]){
stack.pop();
}
left[i] = stack.isEmpty()?
i +1:i-stack.peek();
stack.push(i);
}
//千万记得clear
stack.clear();
//求右边第一个比自己小的距离,求右边反着遍历
for(int i=n-1;i>=0;i--){
while(!stack.isEmpty() && heights[stack.peek()]>=heights[i]){
stack.pop();
}
right[i] = stack.isEmpty()?
heights.length-i:stack.peek()-i;
stack.push(i);
}
int res = 0;
//每条矩形条构造自己的最大矩形
for(int i=0;i<n;i++){
System.out.println(
left[i]+" "+right[i]+" "+heights[i]
);
res=Math.max(((
left[i]>=1?left[i]-1:0)
+(right[i]>=1?right[i]-1:0)
+1 )*heights[i],res);
}
return res;
}
}
然后接下来是42题,这题就有点难了