739.每日温度
题目
请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/daily-temperatures
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解1-单调栈
单调栈就是栈内得元素保持升序或降序
使用场景
通常是一维数组,寻找任意一个元素得右边或者左边第一个比自己大或者小的元素的位置。时间复杂度为O(n)
本道题就是找到一个元素右边第一个比自己大的元素。
单调栈中需要明确几个点
1.单调栈里存放的元素是什么?
单调栈中只需要存放元素的下标i就可以了,如果需要元素就用T[i]获取
2.单调栈里的元素(T[i])是递增还是递减的(顺序为从栈头到栈底)
这里使用递增,因为我们需要找到右边第一个比它大的,该元素比栈头元素大,说明栈头找到了第一个比它大的,它就需要出栈了,那么进栈的就是小的。所以从栈底到栈头应该是递减的顺序。
3. 使用单调栈主要有三个条件判断
当前元素T[i]小于当前栈顶元素T[st.peek()]
当前元素T[i]等于当前栈顶元素T[st.peek()]
当前元素T[i]大于当前栈顶元素T[st.peek()]
举例
temperatures = [73,74,75,71,69,72]
i=0,栈空,0入栈
st:0
i=1,T[i]=74,T[st.peek()]=73, T[i]>T[st.peek()],result[st.peek()]=i-st.peek()=1-0=1,i=0出栈。栈空,i=1入栈
st:1
result:1
i=2,T[i]=75,T[st.peek()]=74, T[i]>T[st.peek()],result[st.peek()]=i-st.peek()=2-1=1,i=1出栈。栈空,i=2入栈
st:2
result:1,1
i=3,T[i]=71,T[st.peek()]=75, T[i]<T[st.peek()],i=3入栈。
st:2,3
result:1,1,0(默认初始化的值)
i=4,T[i]=69,T[st.peek()]=71, T[i]<T[st.peek()],i=4入栈。
st:2,3,4
result:1,1,0(默认初始化的值),0(默认初始化的值)
i=5,T[i]=72,T[st.peek()]=69, T[i]>T[st.peek()],result[st.peek()]=i-st.peek()=5-4=1,i=4出栈。
st:2,3
result:1,1,0(默认初始化的值),0(默认初始化的值),1
栈不为空,继续比较。
T[st.peek()]=71,T[i]>T[st.peek()],result[st.peek()]=i-st.peek()=5-3=2,i=3出栈
st:2
result:1,1,0(默认初始化的值),2,1
栈不为空,继续比较。
T[st.peek()]=75, T[i]<T[st.peek()],i=5入栈。
循环结束,栈里坐标对应的result坐标值保持初始化值0。
总结:T[i]>T[st.peek()]的出栈。栈为空或T[i]<=T[st.peek()]时入栈。
代码
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int len = temperatures.length;
int [] result = new int [len];
if(len==1) return result;
Stack<Integer> st = new Stack<>();
st.push(0);
for(int i=1;i<len;i++){
while(!st.empty() && temperatures[i]>temperatures[st.peek()]){
result[st.peek()] = i - st.peek();
st.pop();
}
st.push(i);
}
return result;
}
}
题解2
如果从前往后遍历,那么很多位置需要重复遍历。怎样减少遍历次数呢?从后往前遍历,计算过的位置就不需要再计算了。
从后往前遍历
- 如果T[j]的值大于T[i]的值,那么j就是i右边第一个比它大的,result[i] = j - i;
- 如果T[j]的值小于T[i]的值,且result[j] = 0,说明j的右边没有比它还大的了,T[i]比T[j]还大,result[i] = 0
- 如果T[j]的值小于T[i]的值,且result[j] ≠ 0,假设result[j]=5,意思是j的右边T[j+5]的值比T[j]的值大,[j-1,j-4]都<T[j],T[j]<T[I]说明该区间肯定是<T[i]的,那么T[i]应该和T[j+5]比较。这样就可以减少比较次数了。
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int length = temperatures.length;
int[] result = new int[length];
//从右向左遍历
for (int i = length - 2; i >= 0; i--) {
// j+= result[j]是利用已经有的结果进行跳跃
for (int j = i + 1; j < length; j+= result[j]) {
if (temperatures[j] > temperatures[i]) {
result[i] = j - i;
break;
} else if (result[j] == 0) { //遇到0表示后面不会有更大的值,那当然当前值就应该也为0
result[i] = 0;
break;
}
}
}
return result;
}
}