题目
标题和出处
标题:最大宽度坡
出处: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} 2≤nums.length≤5×104
- 0 ≤ nums[i] ≤ 5 × 10 4 \texttt{0} \le \texttt{nums[i]} \le \texttt{5} \times \texttt{10}^\texttt{4} 0≤nums[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 j−1 的全部下标,找到最小的满足 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 j−i>j−p,以 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 q−i>q−p,因此以 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 j−i 更新坡的最大宽度,重复该操作直到栈为空或者栈顶下标对应的元素大于 nums [ j ] \textit{nums}[j] nums[j]。该做法的正确性说明如下。
-
对于下标 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 出栈,在出栈的同时更新坡的最大宽度。
-
假设存在下标 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 结尾的坡的最大宽度。
-
假设存在下标 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。