力扣第739题,每日温度 请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。 示例 1: 输入: temperatures = [73,74,75,71,69,72,76,73] 输出: [1,1,4,2,1,1,0,0]
暴力法
首先是暴力法,直接复制一个评论里的
int[] result = new int[temperatures.length];
for (int i = 0; i < temperatures.length; i++) {
for (int j = i + 1; j < temperatures.length; j++) {
if (temperatures[i] < temperatures[j]) {
result[i] = j - i;
break; // 已经找到了就直接break,进入下一循环
}
}
}
return result;
这种方法也比较好理解,从数组第一个元素开始遍历,对每一个元素,向后线性遍历,找到第一个比它大的元素,设置result数组对应的值,break这次循环。
官方题解的暴力法
int len = temperatures.length;
int[] res = new int[len];
int[] next = new int[101];
Arrays.fill(next,Integer.MAX_VALUE);
for(int i=len-1;i>-1;--i){
int index = Integer.MAX_VALUE;
for(int t=temperatures[i]+1;t<101;++t)
index = Math.min(index,next[t]);
if(index<Integer.MAX_VALUE)
res[i] = index-i;
next[temperatures[i]] = i;
}
return res;
这个方法被官方题解称为“暴力”,不过感觉比上面那种暴力法优化了很多,最后提交结果来看,和单调栈法的性能是差不多的。。。 这种方法从右向左遍历,用一个next数组记录截至遍历到某位置时,各个温度温度出现的最左的坐标。。。并且这个next数组里元素均初始化为Integer.MAX_VALUE,这样的话,当我们遍历到某温度tempretures[i]时,只需要对next数组进行遍历,遍历从tempretures[i]+1开始,这样遍历的所有温度都是比tempretures[i]大的,然后通过对next数组遍历,容易得到这些比tempretures[i]大的温度的目前的最左的坐标index,也就是满足问题的解,最后进行next[temperatures[i]]=i,更新当前温度的next数组,如果index=Integer.MAX_VALUE,说明比当前温度大的温度都还没有遍历到,也就是说,此温度右边没有比它更大的温度,那么就不做操作,Java里int数组元素值默认为0。 为什么从右向左遍历?因为对每个温度来说,符合条件的解只和它右边的数有关,所以咱们从右向左遍历,每次用next数组记录各温度当前出现的最左位置,再进行next数组的遍历,是可以奏效的。
单调栈法
int len = temperatures.length;
int[] res = new int[len];
Deque<Integer> stack = new LinkedList<>();
for(int i=0;i<len;++i){
while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
res[stack.peek()] = i-stack.peek();
stack.pop();
}
stack.push(i);
}
return res;
这种方法就是借助一个栈,它的思想就是,从左向右遍历数组,每次往栈里加入新元素时,先把栈里所有比此元素小的元素弹出,并更新这些元素相应的解,这样栈里的元素都是非严格递减的(可以有相同的元素)。 这种方法可以这样理解,假设没有这个栈,我们从左向右遍历数组,每次遍历到某一元素,就查找它左边所有比它小的元素,再用一个visited数组记录哪些元素的解已经得出了,下次不再遍历,这是一个双重循环(其实就是暴力法)。那么现在引入了栈这个辅助结构,其实做的和上面还是一样的事,每次到一个元素,把所有比它小的元素都设置了并且弹出,下次就不用对它操作,而且也可以保证每个元素被右边第一个比它大的元素弹出。
我自己的方法
int len = temperatures.length;
int[] res = new int[len];
for(int i=len-2;i>-1;--i){
if(temperatures[i]<temperatures[i+1])
res[i] = 1;
else if(temperatures[i]==temperatures[i+1])
if(res[i+1]==0)
res[i] = 0;
else res[i] = res[i+1] + 1;
else{
int count = res[i+1]+1;
while(count+i<len){
if(temperatures[i+count]>temperatures[i]){
res[i] = count;
break;
}
else if(temperatures[i+count]==temperatures[i]){
if(res[i+count]==0)
res[i] = 0;
else res[i] = count + res[i+count];
break;
}
else{
if(res[i+count]==0){
res[i] =0;
break;
}
count += res[i+count];
}
}
}
}
return res;
这个方法是我当时自己写出来的方法,在题解的评论里也看到类似的了,写的代码比较冗长,主要是边界条件有点多,但是效率不错,从提交情况来看,用时是单调栈的1/3左右,还是可以的! 首先由于每个元素对应的解其实之和它右边的解有关,所以我们从右向左遍历(参考暴力法),但是我们是否可以不要每个元素都线性的比?其实那样会多比很多次。那么我们可以讨论一下当前元素i和它右边第一个元素i+1的关系, 如果temperatures[i]<temperatures[i+1],那太好了,我们直接找到了,直接设置解为1就行了; 如果temperatures[i]=temperatures[i+1],那res[i]就是res[i+1]+1,也很好理解,不过要注意,如果res[i+1]为0,说明没有比temperatures[i+1]更大的数,那么它俩相等,res[i]也是0 如果temperatures[i]>temperatures[i+1],那么我们直接找temperatures[i+1+res[i+1]],为什么呢?因为temperatures[i+1+res[i+1]]是右边第一个比res[i+1]大的数,temperatures[i+1]和temperatures[i+1+res[i+1]]之间的这些数都小于等于temperatures[i+1],没有比较的必要了,直接跳过就行了,和temperatures[i+1+res[i+1]]比较也是同理,每次跳着比,注意下不要越界了就行。 不过这个代码着实冗长,我在题解里看到写的更简洁的如下:
int[] res = new int[len];
for(int i=len-2;i>-1;--i){
for(int j=i+1;j<len;j+=res[j]){
if(temperatures[j]>temperatures[i]){
res[i] = j-i;
break;
}
else{
if(res[j]==0){
res[i]=0;
break;
}
}
}
}
return res;
其实和我是一个意思,不过人家简洁很多 `(>﹏<)′ 第一次写,其实我分段了而且首行缩进了,不过在预览区没看见效果,不知道发出来能不能有缩进效果