503.下一个更大元素II
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
- 输入: [1,2,1]
- 输出: [2,-1,2]
- 解释: 第一个 1 的下一个更大的数是 2;数字 2 找不到下一个更大的数;第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
提示:
- 1 <= nums.length <= 10^4
- -10^9 <= nums[i] <= 10^9
本题和每日温度思路基本一致,多出一步对于循环数组的处理。遇到循环数组,第一个想法就是取数组长度的模,即i%len
class Solution {
public int[] nextGreaterElements(int[] nums) {
Stack<Integer> stack = new Stack<>();
int len=nums.length;
int[] res = new int[len];
Arrays.fill(res, -1);
for(int i=0;i<len*2;i++){
while(!stack.isEmpty() && nums[stack.peek()]<nums[i%len]){
res[stack.peek()]=nums[i%len];
System.out.print(i+" ");
System.out.println(res[stack.peek()]);
stack.pop();
}
stack.push(i%len);
}
return res;
}
}
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
- 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
- 输出:6
- 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
- 输入:height = [4,2,0,3,2,5]
- 输出:9
本题需要计算能接到多少雨水,也就是计算合理的柱子数目及其容量。合理的柱子需要满足下面的条件:
- 同时拥有左右两侧
- 柱子能接雨水的高度取决于矮柱子的高度
那么问题的关键在于,如何判断柱子的左右边界?如何计算容量?
首先更推荐按照列来计算容量,这样子宽度一定为1,把每一列满足条件的高度计算出来即可。
对于确定左右边界,需要分别从左和从右找到最高的列。
第一个柱子和最后一个柱子无法盛雨水
对于列1:
左边没有比当前高的柱子,所以无法盛雨水
对于列2:
- 左边最高的柱子为列1,高度为1
- 右边最高的柱子为列7,高度为3
- 列2高度为0
- 所以列2能容纳的雨水容量为:min(列1高度,列7高度)-列2高度=1-0=1
对于列3:
左边没有比它更高的柱子,所以无法盛雨水
对于列4:
- 左边最高的柱子为列3,高度为2
- 右边最高的柱子为列7,高度为3
- 列4高度为1
- 所以列4能容纳雨水容量为:min(列3高度,列7高度)-列4高度=2-1=1
以此类推。但是这个思路需要消耗O(n^2)的时间复杂度,测试用例不通过
class Solution {
public int trap(int[] height) {
if(height==null) return 0;
int sum=0;
for(int i=1;i<height.length-1;i++){
int lH=height[i];
int rH=height[i];
for(int r=i+1;r<height.length;r++){ //找右边最大的边
rH=height[r]>rH?height[r]:rH;
}
for(int l=i-1;l>=0;i--){// 找左边最大的边
lH=height[l]>lH?height[l]:lH;
}
int h=Math.min(lH,rH)-height[i];
if(h>0) sum+=h;
}
return sum;
}
}
可以看到本题本质依然是寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,接雨水这道题目,我们正需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积,所以还是可以考虑用到单调栈。
将本题看作求每根柱子可以容纳雨水的体积,高度为下图所示:
宽度如下图:
先将a,b,c压入栈中,此时遇到了一个比栈顶e的高度更大的c,
则e柱能容纳的水高度为min(b,c)-e,宽度为c的下标-b的下标;
b柱能容纳的雨水高度为min(a,c)-b,宽度为 c的下标-a的下标;
最后能容纳的雨水容量为高*宽
class Solution {
public int trap(int[] height) {
if(height==null) return 0;
int sum=0;
Stack<Integer> stack= new Stack<>();
int len=height.length;
stack.push(0);
for(int i=1;i<len;i++){
while(!stack.isEmpty() && height[stack.peek()]<height[i]){
int top=height[stack.peek()]; //先记录栈顶元素的高度
stack.pop(); //将栈顶元素弹出
if(!stack.isEmpty()){//说明有左边
int h=Math.min(height[stack.peek()], height[i])-top; //计算高度
int w=i-stack.peek()-1;
sum+=h*w;
}
}
stack.push(i);
}
return sum;
}
}
84.柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为1。求在该柱状图中,能够勾勒出来的矩形的最大面积。
本题看起来和接雨水很类似,都是求面积,但是接雨水是找每个柱子左右两边第一个大于该柱子高度的柱子,分别求宽和高然后相乘,而本题是找左右两侧最近的高度小于当前高度的柱子,所以本题应该是从栈顶到栈尾单调递减栈。
class Solution {
public int largestRectangleArea(int[] heights) {
int max=0;
Stack<Integer> stack = new Stack<>();
int len=heights.length;
int[] newHeight = new int[len+ 2]; //定义一个新数组,加入头和尾
System.arraycopy(heights, 0, newHeight, 1, len);
for(int i=0;i<newHeight.length;i++){
while (!stack.isEmpty() && newHeight[i] < newHeight[stack.peek()]) {//需要弹
int h=newHeight[stack.peek()];
stack.pop();
int w=i-stack.peek()-1;
max=max>h*w?max:h*w;
}
stack.push(i);
}
return max;
}
}