739. 每日温度
思路:单调栈
// 时间复杂度O(n)~O(n^2)
// 空间复杂度O(n)
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
if(temperatures.length == 1)
return new int[]{0};
int n = temperatures.length;
int[] answer = new int[n];
List<Integer> list = new ArrayList<>();
for(int i=n-1; i>=0; i--){
// 注意temperatures 数组中可能会存在重复元素,因此比较大小是大于等于,从而找到右侧大于自己的元素,而不能把等于自己的元素也算进去
while(list.size()>0 && temperatures[i] >= temperatures[list.get(list.size()-1)]){
list.remove(list.size()-1);
}
if(list.size() == 0)
answer[i] = 0;
else{
// 取得当前大于nums[i]的首个右侧元素,即栈首,但存储的是那个元素的下标
int idx = list.get(list.size()-1);
answer[i] = idx-i;
}
list.add(i);
}
return answer;
}
}
496.下一个更大元素 I
思路:单调栈
// 时间复杂度O(n)~O(n^2)
// 空间复杂度O(n)
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
// // 能想到的首要的解题策略是暴力法,时间复杂度是O(n^2)
// // 采用单调栈的方法进行求解
// // 针对找右侧出现第一个大于目标数字的数,我们遍历的顺序是对目标数组从前往后遍历,且单调栈构建之后是从栈首到栈底是递增的
Map<Integer, Integer> map = new HashMap<>();
Stack<Integer> stack = new Stack<>();
int[] ans = new int[nums1.length];
// // 这一步操作非常的关键,如果没有这步操作的话,那些被压入栈但是不出栈的元素将始终得不到其在nums1是否存在的判断,那么ans中相应的位置也不会得到赋值
// Arrays.fill(ans, -1);
// // 一定是数作为键,下标作为value,因为nums1和nums2在数值上是存在交集,而不是在下标上
for(int i=0; i<nums1.length; i++)
map.put(nums1[i], i);
// for(int i=0; i<nums2.length; i++){
// // 出现更大的元素,则需要对栈开始出栈元素
// while(!stack.isEmpty() && nums2[i] > nums2[stack.peek()]){
// // 获取被出栈的元素,并且对于ans的赋值就是在此操作
// int idx = stack.pop();
// if(map.containsKey(nums2[idx])){
// int j = map.get(nums2[idx]);
// ans[j] = nums2[i];
// }
// }
// stack.push(i);
// }
// return ans;
// 使用与739题保持一致的方式进行求解
for(int i=nums2.length-1; i>=0; i--){
while(!stack.isEmpty() && nums2[i] > nums2[stack.peek()])
stack.pop();
// 栈本身就为空,或者是把所有元素都弹出了,那么自然当前的这个数字在nums2的当前位置的右侧没有更大的元素了
if(stack.isEmpty()){
if(map.containsKey(nums2[i]))
ans[map.get(nums2[i])] = -1;
}
else{
// 可以在栈内找到一个更大的元素,由于数组是从右侧开始遍历的,所以一定是位于右侧的,并且栈内是递增的,所以一定保存的最近大于当前nums2[i]的,因为如果是nums2[i] = 1, 4,2应该选4, 2,4应该选2
int idx = stack.peek();
if(map.containsKey(nums2[i]))
ans[map.get(nums2[i])] = nums2[idx];
}
stack.push(i);
}
return ans;
}
}
心得:
- 单调栈,找右侧第一个大的元素。从左向右遍历,出栈小于当前位置元素的栈内元素,且在出栈时进行结果的更新。如果不出栈的元素,则得不到更新,因此依赖初始化动作去赋值;
- 从右向左遍历,每一个元素仍然进行批量出栈,在批量出栈动作结束后,再进行对当前元素位置结果操作的一次更新。此方式可以根据批量出栈操作后,栈内是否还存在元素来判断当前位置元素是否有可行的目标,是填入-1还是其他下标值。
- 总体而言第一种思路更直观。第二种方法更为的全面。