单调栈题目:最大宽度坡

题目

标题和出处

标题:最大宽度坡

出处:962. 最大宽度坡

难度

7 级

题目描述

要求

整数数组 nums \texttt{nums} nums 中的坡是数组对 (i,   j) \texttt{(i, j)} (i, j),其中 i < j \texttt{i} < \texttt{j} i<j nums[i] ≤ nums[j] \texttt{nums[i]} \le \texttt{nums[j]} nums[i]nums[j]。这样的坡的宽度为 j   -   i \texttt{j - i} j - i

给定整数数组 nums \texttt{nums} nums,找出 nums \texttt{nums} nums 中的坡的最大宽度。如果 nums \texttt{nums} nums 中不存在坡,返回 0 \texttt{0} 0

示例

示例 1:

输入: nums   =   [6,0,8,2,1,5] \texttt{nums = [6,0,8,2,1,5]} nums = [6,0,8,2,1,5]
输出: 4 \texttt{4} 4
解释:最大宽度的坡为 (i,   j)   =   (1,   5) \texttt{(i, j) = (1, 5)} (i, j) = (1, 5) nums[1]   =   0 \texttt{nums[1] = 0} nums[1] = 0 nums[5]   =   5 \texttt{nums[5] = 5} nums[5] = 5

示例 2:

输入: nums   =   [9,8,1,0,1,9,4,0,4,1] \texttt{nums = [9,8,1,0,1,9,4,0,4,1]} nums = [9,8,1,0,1,9,4,0,4,1]
输出: 7 \texttt{7} 7
解释:最大宽度的坡为 (i,   j)   =   (2,   9) \texttt{(i, j) = (2, 9)} (i, j) = (2, 9) nums[2]   =   1 \texttt{nums[2] = 1} nums[2] = 1 nums[9]   =   1 \texttt{nums[9] = 1} nums[9] = 1

数据范围

  • 2 ≤ nums.length ≤ 5 × 10 4 \texttt{2} \le \texttt{nums.length} \le \texttt{5} \times \texttt{10}^\texttt{4} 2nums.length5×104
  • 0 ≤ nums[i] ≤ 5 × 10 4 \texttt{0} \le \texttt{nums[i]} \le \texttt{5} \times \texttt{10}^\texttt{4} 0nums[i]5×104

解法

思路和算法

为了得到数组 nums \textit{nums} nums 中的坡的最大宽度,需要对每个下标 j j j 找到最小的下标 i i i,满足 i < j i < j i<j nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j]

最简单的做法是对于每个下标 j j j,遍历从 0 0 0 j − 1 j - 1 j1 的全部下标,找到最小的满足 nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j] 的下标 i i i。假设数组 nums \textit{nums} nums 的长度是 n n n,该做法的时间复杂度是 O ( n 2 ) O(n^2) O(n2),会超出时间限制,因此需要优化。

假设存在一个坡 ( i , j ) (i, j) (i,j),则有 i < j i < j i<j nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j]。如果存在下标 p p p q q q 满足 i < p < j < q i < p < j < q i<p<j<q nums [ p ] ≤ nums [ q ] \textit{nums}[p] \le \textit{nums}[q] nums[p]nums[q],考虑 nums [ i ] \textit{nums}[i] nums[i] nums [ p ] \textit{nums}[p] nums[p] 的大小关系。

  • 如果 nums [ i ] ≤ nums [ p ] \textit{nums}[i] \le \textit{nums}[p] nums[i]nums[p],则即使有 nums [ p ] ≤ nums [ j ] \textit{nums}[p] \le \textit{nums}[j] nums[p]nums[j],以 j j j 结尾的最大宽度坡(即所有以 j j j 结尾的坡中宽度最大的坡)也不可能以 p p p 开始,因为 j − i > j − p j - i > j - p ji>jp,以 i i i 开始的坡宽度更大,对于 q q q 而言, nums [ i ] \textit{nums}[i] nums[i] nums [ p ] \textit{nums}[p] nums[p] 都小于等于 nums [ q ] \textit{nums}[q] nums[q],由于 q − i > q − p q - i > q - p qi>qp,因此以 q q q 结尾的最大宽度坡不可能以 p p p 开始。

  • 如果 nums [ i ] > nums [ p ] \textit{nums}[i] > \textit{nums}[p] nums[i]>nums[p],则虽然以 j j j 结尾的最大宽度坡不可能以 p p p 开始,但是由于 nums [ q ] \textit{nums}[q] nums[q] 可能小于 nums [ i ] \textit{nums}[i] nums[i],因此以 q q q 结尾的最大宽度坡可能以 p p p 开始。

根据上述分析可知,下标 i i i 可能是最大宽度坡的开始下标的条件是对于任意小于 i i i 的下标 k k k 都有 nums [ k ] > nums [ i ] \textit{nums}[k] > \textit{nums}[i] nums[k]>nums[i]。如果存在一个坡 ( i , j ) (i, j) (i,j) 且存在小于 i i i 的下标 k k k 满足 nums [ k ] ≤ nums [ i ] \textit{nums}[k] \le \textit{nums}[i] nums[k]nums[i],则 ( k , j ) (k, j) (k,j) 一定是坡,且坡 ( k , j ) (k, j) (k,j) 的宽度比坡 ( i , j ) (i, j) (i,j) 的宽度更大。

如果可以排除不可能是最大宽度坡的开始下标的下标,则可以降低时间复杂度。可以使用单调栈存储可能是最大宽度坡的开始下标的全部下标,单调栈满足从栈底到栈顶的下标对应的元素单调递减。

从左到右遍历数组 nums \textit{nums} nums,对于每个下标 i i i,当且仅当栈为空或者栈顶下标对应的元素大于 nums [ i ] \textit{nums}[i] nums[i] 时,将 i i i 入栈。遍历结束之后,栈内的每个下标 i i i 都满足对于任意小于 i i i 的下标 k k k 都有 nums [ k ] > nums [ i ] \textit{nums}[k] > \textit{nums}[i] nums[k]>nums[i]

然后从右到左遍历数组 nums \textit{nums} nums,对于每个下标 j j j,需要找到最小的下标 i i i 使得 nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j]。具体做法是,当栈不为空且栈顶下标对应的元素小于等于 nums [ j ] \textit{nums}[j] nums[j] 时,令栈顶下标为 i i i,将 i i i 出栈,并用 j − i j - i ji 更新坡的最大宽度,重复该操作直到栈为空或者栈顶下标对应的元素大于 nums [ j ] \textit{nums}[j] nums[j]。该做法的正确性说明如下。

  1. 对于下标 j j j,如果有多个小于 j j j 的下标对应的元素都小于等于 nums [ j ] \textit{nums}[j] nums[j],则其中最小的下标和 j j j 组成以 j j j 结尾的最大宽度坡。由于单调栈的下标入栈顺序为下标递增顺序,因此越接近栈底的下标越小,和 j j j 组成的坡的宽度也越大。为了得到以下标 j j j 结尾的最大宽度坡,应在栈内找到最小的下标 i i i 使得 nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j],因此应该将全部满足 nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j] 的下标 i i i 出栈,在出栈的同时更新坡的最大宽度。

  2. 假设存在下标 k k k 满足 k < j k < j k<j nums [ k ] ≤ nums [ j ] \textit{nums}[k] \le \textit{nums}[j] nums[k]nums[j],则任何以 k k k 结尾的坡的开始下标都可以是以 j j j 结尾的坡的开始下标,因此以 k k k 结尾的坡的最大宽度一定小于以 j j j 结尾的坡的最大宽度。

  3. 假设存在下标 k k k 满足 k < j k < j k<j nums [ k ] > nums [ j ] \textit{nums}[k] > \textit{nums}[j] nums[k]>nums[j],则可能存在下标 p p p 满足 p < k p < k p<k nums [ j ] < nums [ p ] ≤ nums [ k ] \textit{nums}[j] < \textit{nums}[p] \le \textit{nums}[k] nums[j]<nums[p]nums[k],此时下标 p p p 可以是以 k k k 结尾的坡的开始下标,但是不可以是以 j j j 结尾的坡的开始下标。在遍历到 k k k 时,计算以 k k k 结尾的最大宽度坡一定会将 p p p 出栈。

代码

class Solution {
    public int maxWidthRamp(int[] nums) {
        int maxWidth = 0;
        Deque<Integer> stack = new ArrayDeque<Integer>();
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            int num = nums[i];
            if (stack.isEmpty() || nums[stack.peek()] > num) {
                stack.push(i);
            }
        }
        for (int j = length - 1; j > 0; j--) {
            int num = nums[j];
            while (!stack.isEmpty() && nums[stack.peek()] <= num) {
                int width = j - stack.pop();
                maxWidth = Math.max(maxWidth, width);
            }
        }
        return maxWidth;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要从左到右遍历数组 nums \textit{nums} nums 将下标入单调栈,然后从右到左遍历数组 nums \textit{nums} nums 计算最大宽度。由于每个下标最多入栈和出栈各一次,因此时间复杂度是 O ( n ) O(n) O(n)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值