单调栈
No.1
739.每日温度. - 力扣(LeetCode)
给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0
来代替。
示例 1:
输入: temperatures
= [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:
输入: temperatures = [30,40,50,60] 输出: [1,1,1,0]
示例 3:
输入: temperatures = [30,60,90] 输出: [1,1,0]
提示:
1 <= temperatures.length <= 105
30 <= temperatures[i] <= 100
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
// 获取温度数组的长度
int len = temperatures.length;
// 用来存储结果数组,每个元素存储第 i 天后有几天温度升高
int[] ans = new int[len];
// 创建一个栈用于存储索引。栈中的元素代表温度尚未找到升高天数的索引
Deque<Integer> stk = new ArrayDeque<>();
// 遍历温度数组
for (int i = 0; i < len; i++) {
int t = temperatures[i]; // 当前的温度
// 当栈不为空,且当前温度比栈顶所记录的索引的温度高时,进入循环
while (!stk.isEmpty() && t > temperatures[stk.peek()]) {
// 栈顶的索引所对应的温度小于当前温度,所以弹出栈顶元素
int j = stk.pop();
// 计算当前温度超过之前温度的间隔天数
ans[j] = i - j;
}
// 当前温度的索引入栈,等待后续更高温度的判断
stk.push(i);
}
// 返回结果数组,表示每一天距离下一个温度升高的天数
return ans;
}
}
ans[j]
的含义:
j
是从栈中弹出的某一天的索引,这一天的温度较低,现在找到了比它温度更高的那一天i
。- 我们要填充的是 第
j
天的答案,即:ans[j]
应该等于i - j
,表示从第j
天到第i
天经历的天数。
关键点:
i
是比j
天温度更高的那一天,j
是温度较低并等待找到更高温度的那一天。你要更新的是第j
天的答案,所以我们写ans[j]
。ans[j] = i - j
,意思是:第j
天之后,温度升高的天数是从i
天到j
天之间的天数差(i - j
)。
- ArrayList:
- 基于动态数组的实现,随机访问和遍历性能优越。
- 适合频繁查询元素的场景。
- LinkedList:
- 基于双向链表的实现,适合频繁插入和删除操作(特别是头部和尾部的操作)。
- 实现了
Deque
接口,也可用作栈或队列。
- ArrayDeque:
- 头部和尾部的插入和删除操作:平均时间复杂度为 O(1)。
- 随机访问元素:不支持通过索引随机访问,必须通过迭代遍历,时间复杂度为 O(n)。
- ArrayList:
- 随机访问元素:通过索引直接访问元素,时间复杂度为 O(1)。
- 尾部插入或删除元素:平均时间复杂度为 O(1)。
- 头部或中间插入和删除元素:由于需要移动其他元素,时间复杂度为 O(n)。
3. 线程安全性
- ArrayDeque 和 ArrayList 都不是线程安全的。如果在多线程环境下使用它们,需要手动同步操作。
4. 扩容机制
- ArrayDeque 和 ArrayList 都是基于动态数组实现的。随着元素的增加,它们都会自动扩容(通常是当前容量的1.5倍或2倍)。但是扩容时性能会下降,因为需要重新分配更大的数组并复制已有元素。
5. 使用限制
-
ArrayDeque:
- 不允许存储
null
值。任何null
元素的插入都会抛出NullPointerException
。 - 更适合用作栈或队列。
- 不允许存储
-
ArrayList:
- 允许存储
null
值,null
可以被视为一个有效的元素。
- 允许存储
6. 总结
- 如果你需要双端操作(即从头部和尾部都可以高效插入和删除)的数据结构,使用 ArrayDeque。
- 如果你需要随机访问元素,并且主要是在尾部操作元素,使用 ArrayList。
代码示例:
-
ArrayDeque 用作栈:
ArrayDeque<Integer> stack = new ArrayDeque<>(); stack.push(1); // 入栈 stack.push(2); stack.push(3); System.out.println(stack.pop()); // 出栈, 输出3
- ArrayList 用作列表:
ArrayList<Integer> list = new ArrayList<>();
list.add(1); // 尾部添加
list.add(2);
list.add(3);
System.out.println(list.get(0)); // 随机访问, 输出1
No.2
962.最大宽度坡. - 力扣(LeetCode)
给定一个整数数组 A
,坡是元组 (i, j)
,其中 i < j
且 A[i] <= A[j]
。这样的坡的宽度为 j - i
。
找出 A
中的坡的最大宽度,如果不存在,返回 0 。
示例 1:
输入:[6,0,8,2,1,5] 输出:4 解释: 最大宽度的坡为 (i, j) = (1, 5): A[1] = 0 且 A[5] = 5.
示例 2:
输入:[9,8,1,0,1,9,4,0,4,1] 输出:7 解释: 最大宽度的坡为 (i, j) = (2, 9): A[2] = 1 且 A[9] = 1.
提示:
2 <= A.length <= 50000
0 <= A[i] <= 50000
public class MaxWidthRamp {
public int maxWidthRamp(int[] A) {
Stack<Integer> stack = new Stack<>();
int n = A.length;
// 第一次遍历数组,构建单调递减栈
for (int i = 0; i < n; i++) {
// 检查栈是否为空,或者当前元素值小于栈顶元素对应的值
if (stack.isEmpty() || A[stack.peek()] > A[i]) {
stack.push(i); // 将当前元素索引入栈
}
}
int maxWidth = 0;
// 第二次从右到左遍历,寻找最大宽度坡度
for (int j = n - 1; j >= 0; j--) {
// 当栈不为空且当前元素值大于或等于栈顶元素值时,计算坡度宽度
while (!stack.isEmpty() && A[stack.peek()] <= A[j]) {
maxWidth = Math.max(maxWidth, j - stack.pop());
}
}
return maxWidth;
}
public static void main(String[] args) {
MaxWidthRamp solution = new MaxWidthRamp();
int[] A = {6, 0, 8, 2, 1, 5};
System.out.println(solution.maxWidthRamp(A)); // 输出:4
}
}