53. 下一个更大元素
// 时间复杂度O(2n)~O(n^2)
// 空间复杂度O(2n)
class Solution {
public int[] nextGreaterElements(int[] nums) {
// 直接以单调栈的思想进行求解
int n = nums.length;
int[] ans = new int[n];
// Stack<Integer> stack = new Stack<>();
ArrayDeque<Integer> stack = new ArrayDeque<>();
// 栈内按照递增的顺序进入元素,越是考后的元素,其值越大,注意次数从后往前遍历时,需要向左找比当前栈顶小的元素入栈,如果出现与栈顶相等的,则需要弹出栈顶,否则nums[i]无法真正找到比自己大的元素,就会停留在和自己一样大小的元素了
for(int i=2*n-1; i>=0; i--){
while(stack.size()>0 && nums[i%n] >= nums[stack.peek()])
stack.pop();
if(i<n)
ans[i] = stack.size() == 0 ? -1: nums[stack.peek()];
stack.push(i%n);
}
// 采用从左至右的遍历方式进行遍历,上种方法效率更好
// Arrays.fill(ans, -1);
// stack.push(0);
// for(int i=1; i<2*n; i++){
// // 此方式,ans的更新是在每一次弹出一个栈内元素时进行,因此从左到右遍历,出栈操作说明碰到了大于栈顶的右侧第一个元素,注意此种情况下是大于,而不是大于等于了
// while(!stack.isEmpty() && nums[i%n] > nums[stack.peek()]){
// int idx = stack.pop();
// ans[idx] = nums[i%n];
// }
// stack.push(i%n);
// }
return ans;
}
}
42. 接雨水
思路:读题明确需要双指针来描述一个长方形,里面可以填入雨水。因此首先使用双指针进行求解;双指针是同侧快慢指针,两个分别维护长方形的一条宽。接下来思考双指针是如何移动的?快指针随着数组遍历而移动,慢指针总是指向height不为零的位置,那么如何在快指针访问的位置适时的赋予慢指针呢?观察求和填雨水的求法,我们需要比较快慢指针所指向位置的高度,选择较小的来计算。因此得出慢指针的更新应当在遇到更高的高度后,指向其。即指向右侧第一个大于当前位置的元素,与单调栈关联。
求解时,采用了从左至右的遍历思路,则在每一次出栈都需要对结果进行更新。单调栈从栈首至栈尾是递增,所以如果出栈的情况,那么一定是可以有雨水可接。但是需要注意栈中存在32101这样的山谷状,此时出栈一次求取的是最山谷部分的雨水量,其他部分可以随着后续出栈操作而更新,但是需要需要减去先前已出栈部分的 高度差differ。特别注意当存在高度差后,当前高度没有把栈内元素完全清空,且栈内剩余元素是大于当前高度的,则此时应该先将栈顶元素与当前位置之间的雨水量求出来,否则就会出现有一块面积遗漏。举个例子4,2,1,3,0,5,如果不额外计算一步的话,橙色块的雨水就没有能够记录下来。
本题的单调栈的规则是这样:
- 大于栈顶元素(都行,因为设置了高度差这个量),则出栈栈顶元素,开始计算栈顶元素下标与当前元素下标之间的雨水量;
- 小于栈顶元素,直接入栈;
// 时间复杂度O(n) ~ O(n^2)
// 空间复杂度O(1)
class Solution {
public int trap(int[] height) {
// 优先双指针(同侧快慢双指针),时间复杂度比较高,可能会超时
int n = height.length;
int amount = 0;
int differ = 0;
ArrayDeque<Integer> stack = new ArrayDeque<>();
// 从左向右遍历形成单调栈
// 初始化
for(int i=0; i<n; i++){
if(height[i] > 0){
stack.push(i);
break;
}
}
if(stack.size() == 0)
return 0;
for(int j=stack.peek()+1; j<n; j++){
int am = 0;
boolean flag = false;
while(!stack.isEmpty() && height[j] > height[stack.peek()]){
// idx一定是比当前j位置的元素来的小的
flag = true;
int idx = stack.pop();
// if(height[idx] == 0)
// zero++;
if(height[idx] > 0){
// 碰到栈内当前最小的高度,与nums[j]之间只会存在0高度
am += (height[idx]-differ)*(j-idx-1);
differ = height[idx];
}
}
// 此时要么栈内已经为空,要么height[j]遇到了一个更大的高度,那么在入栈前,需要将这部分高度差内可以填入的水率先收集起来
if(flag && !stack.isEmpty())
if(height[j] > differ)
am += (height[j]-differ)*(j-stack.peek()-1);
stack.push(j);
differ = 0;
amount += am;
}
return amount;
}
}
其他方法后序再补充,上面的方法是目前先想的一种单调栈解法。