标题:132 模式
出处:456. 132 模式
6 级
给你一个整数数组 nums \texttt{nums} nums,数组中共有 n \texttt{n} n 个整数。 132 \texttt{132} 132 模式的子序列由三个整数 nums[i] \texttt{nums[i]} nums[i]、 nums[j] \texttt{nums[j]} nums[j] 和 nums[k] \texttt{nums[k]} nums[k] 组成,并同时满足: i < j < k \texttt{i < j < k} i < j < k 和 nums[i] < nums[k] < nums[j] \texttt{nums[i] < nums[k] < nums[j]} nums[i] < nums[k] < nums[j]。
如果 nums \texttt{nums} nums 中存在 132 \texttt{132} 132 模式的子序列,返回 true \texttt{true} true;否则,返回 false \texttt{false} false。
示例 1:
输入: nums = [1,2,3,4] \texttt{nums = [1,2,3,4]} nums = [1,2,3,4]
输出: false \texttt{false} false
解释:序列中不存在 132 \texttt{132} 132 模式的子序列。
示例 2:
输入: nums = [3,1,4,2] \texttt{nums = [3,1,4,2]} nums = [3,1,4,2]
输出: true \texttt{true} true
解释:序列中有 1 \texttt{1} 1 个 132 \texttt{132} 132 模式的子序列: [1, 4, 2] \texttt{[1, 4, 2]} [1, 4, 2]。
示例 3:
输入: nums = [-1,3,2,0] \texttt{nums = [-1,3,2,0]} nums = [-1,3,2,0]
输出: true \texttt{true} true
解释:序列中有 3 \texttt{3} 3 个 132 \texttt{132} 132 模式的的子序列: [-1, 3, 2] \texttt{[-1, 3, 2]} [-1, 3, 2]、 [-1, 3, 0] \texttt{[-1, 3, 0]} [-1, 3, 0] 和 [-1, 2, 0] \texttt{[-1, 2, 0]} [-1, 2, 0]。
这道题要求判断长度为 n n n 的数组 nums \textit{nums} nums 中是否存在下标三元组 ( i , j , k ) (i, j, k) (i,j,k) 满足 i < j < k i < j < k i<j<k 且 nums [ i ] < nums [ k ] < nums [ j ] \textit{nums}[i] < \textit{nums}[k] < \textit{nums}[j] nums[i]<nums[k]<nums[j]。
由于三元组的最小下标 i i i 对应最小的元素,因此可以在枚举 i i i 的同时寻找符合要求的下标 j j j 和 k k k。
对于下标 j j j 和 k k k,如果满足 j < k j < k j<k 且 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k],则只要存在一个下标 i i i 满足 i < j i < j i<j 且 nums [ i ] < nums [ k ] \textit{nums}[i] < \textit{nums}[k] nums[i]<nums[k],即存在满足要求的下标三元组 ( i , j , k ) (i, j, k) (i,j,k)。因此,可以记录数组 nums \textit{nums} nums 的每个前缀的最小值。
创建长度为 n n n 的数组 minArray \textit{minArray} minArray,对于任意 0 ≤ i < n 0 \le i < n 0≤i<n, minArray [ i ] \textit{minArray}[i] minArray[i] 表示数组 nums \textit{nums} nums 从下标 0 0 0 到下标 i i i 的范围内的最小值。数组 minArray \textit{minArray} minArray 中的元素计算方法是:
minArray [ 0 ] = nums [ 0 ] \textit{minArray}[0] = \textit{nums}[0] minArray[0]=nums[0];
对于 1 ≤ i < n 1 \le i < n 1≤i<n, minArray [ i ] = min ( minArray [ i − 1 ] , nums [ i ] ) \textit{minArray}[i] = \min(\textit{minArray}[i - 1], \textit{nums}[i]) minArray[i]=min(minArray[i−1],nums[i])。
根据数组 minArray \textit{minArray} minArray 的定义可知,对于任意 1 ≤ i < n 1 \le i < n 1≤i<n, minArray [ i − 1 ] ≥ minArray [ i ] \textit{minArray}[i - 1] \ge \textit{minArray}[i] minArray[i−1]≥minArray[i],因此 minArray \textit{minArray} minArray 的元素具有非严格单调性,从左到右遍历时单调递减,从右到左遍历时单调递增。
得到数组 minArray \textit{minArray} minArray 之后,从右到左遍历数组 nums \textit{nums} nums,寻找满足条件的下标三元组。遍历过程中维护单调栈,单调栈存储数组 nums \textit{nums} nums 的元素,满足从栈底到栈顶的元素单调递减(非严格)。
记当前下标为 j j j,需要寻找小于 j j j 的下标 i i i 和大于 j j j 的下标 k k k,满足 nums [ i ] < nums [ k ] < nums [ j ] \textit{nums}[i] < \textit{nums}[k] < \textit{nums}[j] nums[i]<nums[k]<nums[j]。具体做法如下:
如果栈不为空且栈顶元素小于或等于 minArray [ j ] \textit{minArray}[j] minArray[j],则将栈顶元素出栈,重复该操作直到栈为空或栈顶元素大于 minArray [ j ] \textit{minArray}[j] minArray[j];
如果栈不为空且栈顶元素小于 nums [ j ] \textit{nums}[j] nums[j],则返回 true \text{true} true,否则将 nums [ j ] \textit{nums}[j] nums[j] 入栈。
遍历结束时,如果没有发现满足要求的下标三元组,返回 false \text{false} false。
对于上述解法的正确性,需要从两个方面证明,一是只有当找到符合要求的下标三元组时才会返回 true \text{true} true,二是只要存在符合要求的下标三元组就一定会返回 true \text{true} true。
由于对单调栈操作时是从右到左遍历数组 nums \textit{nums} nums,因此当遍历到下标 j j j 时,单调栈内的元素一定是 nums [ j ] \textit{nums}[j] nums[j] 右边的元素,即单调栈内的元素在数组中的下标一定大于 j j j。当单调栈不为空时,假设单调栈的栈顶元素在数组中对应下标 k k k,即栈顶元素是 nums [ k ] \textit{nums}[k] nums[k],则必有 j < k j < k j<k。又由于当遍历到下标 j j j 时,如果单调栈不为空,则单调栈的栈顶元素一定大于 minArray [ j ] \textit{minArray}[j] minArray[j](否则栈顶元素出栈),因此当单调栈不为空时,一定存在下标 i i i,使得 i ≤ j < k i \le j < k i≤j<k 且 nums [ i ] = minArray [ j ] < nums [ k ] \textit{nums}[i] = \textit{minArray}[j] < \textit{nums}[k] nums[i]=minArray[j]<nums[k]。如果 nums [ j ] \textit{nums}[j] nums[j] 大于栈顶元素,则 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k],根据传递性可知 nums [ j ] > nums [ k ] > nums [ i ] \textit{nums}[j] > \textit{nums}[k] > \textit{nums}[i] nums[j]>nums[k]>nums[i],因此 i ≠ j i \ne j i=j,必有 i < j i < j i<j,此时找到一个下标三元组 ( i , j , k ) (i, j, k) (i,j,k),满足 i < j < k i < j < k i<j<k 且 nums [ i ] < nums [ k ] < nums [ j ] \textit{nums}[i] < \textit{nums}[k] < \textit{nums}[j] nums[i]<nums[k]<nums[j],该下标三元组满足 132 132 132 模式,返回 true \text{true} true。
由于当遍历到下标 j j j 时,单调栈内的元素一定大于 minArray [ j ] \textit{minArray}[j] minArray[j],且当 nums [ j ] \textit{nums}[j] nums[j] 大于栈顶元素时即返回 true \text{true} true,因此只有当 nums [ j ] \textit{nums}[j] nums[j] 小于等于栈顶元素时才会将 nums [ j ] \textit{nums}[j] nums[j] 入栈,可知单调栈满足从栈底到栈顶的元素单调递减(非严格)。
根据数组 minArray \textit{minArray} minArray 的定义可知,对于任意 1 ≤ i < n 1 \le i < n 1≤i<n, minArray [ i − 1 ] ≥ minArray [ i ] \textit{minArray}[i - 1] \ge \textit{minArray}[i] minArray[i−1]≥minArray[i],因此在从右到左遍历数组 nums \textit{nums} nums 的过程中,下标 j j j 递减,访问到的 minArray [ j ] \textit{minArray}[j] minArray[j] 的值递增(非严格)。由于单调栈内的元素必须大于 minArray [ j ] \textit{minArray}[j] minArray[j],因此在从右到左遍历过程中,单调栈的栈顶元素允许的最小值在递增,如果单调栈不为空,则栈顶元素 nums [ k ] \textit{nums}[k] nums[k] 一定是数组 nums \textit{nums} nums 位于下标 j j j 右边的元素中的大于 minArray [ j ] \textit{minArray}[j] minArray[j] 的最小元素,只要 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k] 即找到满足要求的下标三元组。如果此时单调栈为空,则不存在满足 j < k j < k j<k 且 minArray [ j ] < nums [ k ] \textit{minArray}[j] < \textit{nums}[k] minArray[j]<nums[k] 的下标 k k k,因此不存在满足要求的下标三元组;如果此时 nums [ j ] \textit{nums}[j] nums[j] 小于等于单调栈的栈顶元素,则虽然存在下标 i i i 和 k k k 满足 i < j < k i < j < k i<j<k 且 nums [ i ] < nums [ k ] \textit{nums}[i] < \textit{nums}[k] nums[i]<nums[k],但是当前下标 j j j 不满足 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k],因此不存在满足要求的下标三元组。根据上述分析可知,对于当前下标 j j j,如果存在满足要求的下标三元组则不会遗漏,如果没有找到将当前下标 j j j 作为中间下标的下标三元组则说明当前下标 j j j 无法作为任何一个下标三元组中的 j j j。
class Solution {
public boolean find132pattern(int[] nums) {
int n = nums.length;
int[] minArray = new int[n];
minArray[0] = nums[0];
for (int i = 1; i < n; i++) {
minArray[i] = Math.min(minArray[i - 1], nums[i]);
}
Deque<Integer> stack = new ArrayDeque<Integer>();
for (int j = n - 1; j >= 0; j--) {
int num = nums[j];
while (!stack.isEmpty() && stack.peek() <= minArray[j]) {
stack.pop();
}
if (!stack.isEmpty() && stack.peek() < num) {
return true;
}
stack.push(num);
}
return false;
}
}
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要从左到右遍历数组 nums \textit{nums} nums 计算数组 minArray \textit{minArray} minArray 的值,然后从右到左遍历 nums \textit{nums} nums 寻找满足要求的下标三元组,由于每个元素最多入栈和出栈各一次,因此时间复杂度是 O ( n ) O(n) O(n)。
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。空间复杂度主要取决于数组 minArray \textit{minArray} minArray 和栈空间,数组 minArray \textit{minArray} minArray 的长度是 n n n,栈内元素个数不会超过 n n n。