• 单调栈题目:每日温度


    题目

    标题和出处

    标题:每日温度

    出处:739. 每日温度

    难度

    6 级

    题目描述

    要求

    给定整数数组 temperatures \texttt{temperatures} temperatures 表示每日温度,返回数组 answer \texttt{answer} answer,其中 answer[i] \texttt{answer[i]} answer[i] 是在第 i \texttt{i} i 天之后出现更高温度需要等待的天数。如果在将来没有更高温度,则 answer[i]   =   0 \texttt{answer[i] = 0} answer[i] = 0

    示例

    示例 1:

    输入: temperatures   =   [73,74,75,71,69,72,76,73] \texttt{temperatures = [73,74,75,71,69,72,76,73]} temperatures = [73,74,75,71,69,72,76,73]
    输出: [1,1,4,2,1,1,0,0] \texttt{[1,1,4,2,1,1,0,0]} [1,1,4,2,1,1,0,0]

    示例 2:

    输入: temperatures   =   [30,40,50,60] \texttt{temperatures = [30,40,50,60]} temperatures = [30,40,50,60]
    输出: [1,1,1,0] \texttt{[1,1,1,0]} [1,1,1,0]

    示例 3:

    输入: temperatures   =   [30,60,90] \texttt{temperatures = [30,60,90]} temperatures = [30,60,90]
    输出: [1,1,0] \texttt{[1,1,0]} [1,1,0]

    数据范围

    • 1 ≤ temperatures.length ≤ 10 5 \texttt{1} \le \texttt{temperatures.length} \le \texttt{10}^\texttt{5} 1temperatures.length105
    • 30 ≤ temperatures[i] ≤ 100 \texttt{30} \le \texttt{temperatures[i]} \le \texttt{100} 30temperatures[i]100

    解法

    思路和算法

    最简单的做法是对于数组 temperatures \textit{temperatures} temperatures 中的每个元素,向后遍历找到第一个比当前元素大的元素,计算下标差。假设数组 temperatures \textit{temperatures} temperatures 的长度是 n n n,上述做法的时间复杂度是 O ( n 2 ) O(n^2) O(n2),会超出时间限制,因此需要优化。

    上述做法中存在重复计算。对于同一个下标 i i i,可能存在 m m m 个下标 j 1 j_1 j1 j m j_m jm,满足对于每个 j k j_k jk 1 ≤ k ≤ m 1 \le k \le m 1km)都有 temperatures [ i ] \textit{temperatures}[i] temperatures[i] temperatures [ j k ] \textit{temperatures}[j_k] temperatures[jk] 后面的第一个比 temperatures [ j k ] \textit{temperatures}[j_k] temperatures[jk] 大的元素,因此对于每个 j k j_k jk 都有 answer [ j k ] = i − j k \textit{answer}[j_k] = i - j_k answer[jk]=ijk。如果按照上述做法,则下标 i i i 被遍历了 m m m 次。

    为了避免重复计算,上述做法应改为:从左到右遍历数组 temperatures \textit{temperatures} temperatures,对于每个下标 i i i,寻找在结果数组 answer \textit{answer} answer 中的所有尚未填入值的下标 j j j(显然 j < i j < i j<i),令 answer [ j ] = i − j \textit{answer}[j] = i - j answer[j]=ij。需要确保结果数组 answer \textit{answer} answer 中的每个元素都用 O ( 1 ) O(1) O(1) 的时间填入一次,总时间复杂度是 O ( n ) O(n) O(n)

    可以使用单调栈实现,单调栈存储数组 temperatures \textit{temperatures} temperatures 的下标,满足从栈底到栈顶的下标对应的元素单调递减(非严格)。

    创建长度为 n n n 的结果数组 answer \textit{answer} answer,初始时对于所有 0 ≤ i < n 0 \le i < n 0i<n 都有 answer [ i ] = 0 \textit{answer}[i] = 0 answer[i]=0。从左到右遍历数组 temperatures \textit{temperatures} temperatures,对于每个下标 i i i,进行如下操作:

    1. 如果栈不为空且栈顶下标对应的温度低于当前温度(即 temperatures [ i ] \textit{temperatures}[i] temperatures[i]),则将栈顶下标出栈,用 j j j 表示出栈下标,则令 answer [ j ] = i − j \textit{answer}[j] = i - j answer[j]=ij,重复该操作直到栈为空或者栈顶下标对应的温度高于或等于当前温度;

    2. i i i 入栈。

    遍历结束时,返回 answer \textit{answer} answer

    上述解法的正确性说明如下。

    1. 上述操作中,在将下标 i i i 入栈之前会将对应温度低于 temperatures [ i ] \textit{temperatures}[i] temperatures[i] 的下标出栈,只有当栈为空或者栈顶下标对应的温度高于或等于 temperatures [ i ] \textit{temperatures}[i] temperatures[i] 时才会将下标 i i i 入栈,因此新入栈的下标对应的温度一定不会高于栈内已有下标对应的温度,单调栈满足从栈底到栈顶的下标对应的元素单调递减。

    2. 当遍历到下标 i i i 时,如果单调栈不为空,则栈内下标一定小于 i i i。假设下标 j j j 在栈内,满足 j < i j < i j<i temperatures [ j ] < temperatures [ i ] \textit{temperatures}[j] < \textit{temperatures}[i] temperatures[j]<temperatures[i],考虑两种情况,分别为 j j j 是栈顶下标和 j j j 不是栈顶下标。

      • 如果 j j j 是栈顶下标,假设存在下标 k k k 满足 j < k < i j < k < i j<k<i temperatures [ j ] < temperatures [ k ] \textit{temperatures}[j] < \textit{temperatures}[k] temperatures[j]<temperatures[k],则在遍历到下标 k k k 时,下标 j j j 就会出栈,下标 j j j 在栈内说明尚未在下标 j j j 右边遇到比 temperatures [ j ] \textit{temperatures}[j] temperatures[j] 大的元素,因此 temperatures [ i ] \textit{temperatures}[i] temperatures[i] temperatures [ j ] \textit{temperatures}[j] temperatures[j] 右边第一个大于 temperatures [ j ] \textit{temperatures}[j] temperatures[j] 的元素。

      • 如果 j j j 不是栈顶下标,假设下标 j ′ j' j 更接近栈顶,则有 j < j ′ j < j' j<j(根据入栈顺序)和 temperatures [ j ] ≥ temperatures [ j ′ ] \textit{temperatures}[j] \ge \textit{temperatures}[j'] temperatures[j]temperatures[j](根据单调栈的性质),当 temperatures [ j ] < temperatures [ i ] \textit{temperatures}[j] < \textit{temperatures}[i] temperatures[j]<temperatures[i] 时必有 temperatures [ j ′ ] < temperatures [ i ] \textit{temperatures}[j'] < \textit{temperatures}[i] temperatures[j]<temperatures[i],因此比下标 j j j 更接近栈顶的下标都会出栈,当 j j j 变成栈顶下标时,即为上一种情况。

    以下是示例 1 的计算过程。

    1. 下标 0 0 0 处的元素是 73 73 73,将 0 0 0 入栈, stack = [ 0 : 73 ] \textit{stack} = [0:73] stack=[0:73],其中左边为栈底,右边为栈顶,栈内存储下标,为了方便阅读加上了下标对应的元素值。

    2. 下标 1 1 1 处的元素是 74 74 74,由于 73 < 74 73 < 74 73<74,因此将 0 0 0 出栈, answer [ 0 ] = 1 − 0 = 1 \textit{answer}[0] = 1 - 0 = 1 answer[0]=10=1,将 1 1 1 入栈, stack = [ 1 : 74 ] \textit{stack} = [1:74] stack=[1:74]

    3. 下标 2 2 2 处的元素是 75 75 75,由于 74 < 75 74 < 75 74<75,因此将 1 1 1 出栈, answer [ 1 ] = 2 − 1 = 1 \textit{answer}[1] = 2 - 1 = 1 answer[1]=21=1,将 2 2 2 入栈, stack = [ 2 : 75 ] \textit{stack} = [2:75] stack=[2:75]

    4. 下标 3 3 3 处的元素是 71 71 71,将 3 3 3 入栈, stack = [ 2 : 75 , 3 : 71 ] \textit{stack} = [2:75, 3:71] stack=[2:75,3:71]

    5. 下标 4 4 4 处的元素是 69 69 69,将 4 4 4 入栈, stack = [ 2 : 75 , 3 : 71 , 4 : 69 ] \textit{stack} = [2:75, 3:71, 4:69] stack=[2:75,3:71,4:69]

    6. 下标 5 5 5 处的元素是 72 72 72,由于 69 < 72 69 < 72 69<72 71 < 72 71 < 72 71<72,因此将 4 4 4 3 3 3 出栈, answer [ 4 ] = 5 − 4 = 1 \textit{answer}[4] = 5 - 4 = 1 answer[4]=54=1 answer [ 3 ] = 5 − 3 = 2 \textit{answer}[3] = 5 - 3 = 2 answer[3]=53=2,将 5 5 5 入栈, stack = [ 2 : 75 , 5 : 72 ] \textit{stack} = [2:75, 5:72] stack=[2:75,5:72]

    7. 下标 6 6 6 处的元素是 76 76 76,由于 72 < 76 72 < 76 72<76 75 < 76 75 < 76 75<76,因此将 5 5 5 2 2 2 出栈, answer [ 5 ] = 6 − 5 = 1 \textit{answer}[5] = 6 - 5 = 1 answer[5]=65=1 answer [ 2 ] = 6 − 2 = 4 \textit{answer}[2] = 6 - 2 = 4 answer[2]=62=4,将 6 6 6 入栈, stack = [ 6 : 76 ] \textit{stack} = [6:76] stack=[6:76]

    8. 下标 7 7 7 处的元素是 73 73 73,将 7 7 7 入栈, stack = [ 6 : 76 , 7 : 73 ] \textit{stack} = [6:76, 7:73] stack=[6:76,7:73]

    结果数组 answer = [ 1 , 1 , 4 , 2 , 1 , 1 , 0 , 0 ] \textit{answer} = [1, 1, 4, 2, 1, 1, 0, 0] answer=[1,1,4,2,1,1,0,0]

    代码

    class Solution {
        public int[] dailyTemperatures(int[] temperatures) {
            int length = temperatures.length;
            int[] answer = new int[length];
            Deque<Integer> stack = new ArrayDeque<Integer>();
            for (int i = 0; i < length; i++) {
                int temperature = temperatures[i];
                while (!stack.isEmpty() && temperatures[stack.peek()] < temperature) {
                    int j = stack.pop();
                    answer[j] = i - j;
                }
                stack.push(i);
            }
            return answer;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    复杂度分析

    • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 temperatures \textit{temperatures} temperatures 的长度。需要从左到右遍历数组 temperatures \textit{temperatures} temperatures 计算结果数组 answer \textit{answer} answer 的值,由于每个下标最多入栈和出栈各一次,结果数组 answer \textit{answer} answer 中的每个元素的计算时间是 O ( 1 ) O(1) O(1),因此时间复杂度是 O ( n ) O(n) O(n)

    • 空间复杂度 O ( n ) O(n) O(n),其中 n n n 是数组 temperatures \textit{temperatures} temperatures 的长度。空间复杂度主要取决于栈空间,栈内元素个数不会超过 n n n

  • 相关阅读:
    手把手教你搭建属于自己的PyQt5-YOLOv5目标检测平台(保姆级教程)
    两数相加——力扣
    Java项目:校园相册管理系统(java+SSM+html+bootstrap+layui+Mysql)
    java中set接口、哈希表、哈希值、HashSet、LinkedHashSet、方法的可变参数
    问题:conda删除虚拟环境,报错no package names supplied
    ABC-270 F - Transportation(kruskal)
    FFMpeg 命令
    MySQL面试题
    基于STM32的简化版智能手表
    【STM32】入门(二):跑马灯-GPIO端口输出控制
  • 原文地址:https://blog.csdn.net/stormsunshine/article/details/121130275