每日温度的几种方法

489 阅读3分钟

力扣第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;

其实和我是一个意思,不过人家简洁很多 `(>﹏<)′ 第一次写,其实我分段了而且首行缩进了,不过在预览区没看见效果,不知道发出来能不能有缩进效果