英文原文
Given an array of n
integers nums
, a 132 pattern is a subsequence of three integers nums[i]
, nums[j]
and nums[k]
such that i < j < k
and nums[i] < nums[k] < nums[j]
.
Return true
if there is a 132 pattern in nums
, otherwise, return false
.
Example 1:
Input: nums = [1,2,3,4] Output: false Explanation: There is no 132 pattern in the sequence.
Example 2:
Input: nums = [3,1,4,2] Output: true Explanation: There is a 132 pattern in the sequence: [1, 4, 2].
Example 3:
Input: nums = [-1,3,2,0] Output: true Explanation: There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0] and [-1, 2, 0].
Constraints:
n == nums.length
1 <= n <= 2 * 105
-109 <= nums[i] <= 109
中文题目
给你一个整数数组 nums
,数组中共有 n
个整数。132 模式的子序列 由三个整数 nums[i]
、nums[j]
和 nums[k]
组成,并同时满足:i < j < k
和 nums[i] < nums[k] < nums[j]
。
如果 nums
中存在 132 模式的子序列 ,返回 true
;否则,返回 false
。
示例 1:
输入:nums = [1,2,3,4] 输出:false 解释:序列中不存在 132 模式的子序列。
示例 2:
输入:nums = [3,1,4,2] 输出:true 解释:序列中有 1 个 132 模式的子序列: [1, 4, 2] 。
示例 3:
输入:nums = [-1,3,2,0] 输出:true 解释:序列中有 3 个 132 模式的的子序列:[-1, 3, 2]、[-1, 3, 0] 和 [-1, 2, 0] 。
提示:
n == nums.length
1 <= n <= 2 * 105
-109 <= nums[i] <= 109
通过代码
高赞题解
基本思路
朴素的做法是分别对三个数进行枚举,这样的做法是 $O(n^3)$ 的,数据范围是 $10^4$,稳稳超时。
事实上,这样的数据范围甚至不足以我们枚举其中两个数,然后优化找第三个数的 $O(n^2)$ 做法。
这时候根据数据范围会联想到树状数组,使用树状数组的复杂度是 $O(n\log{n})$ 的,可以过。但是代码量会较多一点,还需要理解离散化等前置知识。题解也不太好写。
因此,我们可以从 132 的大小特性去分析,如果在确定一个数之后,如何快速找到另外两个数(我们使用 ijk
来代指 132 结构):
枚举
i
:由于i
是 132 结构中最小的数,那么相当于我们要从 i 后面,找到一个对数(j,k)
,使得(j,k)
都满足比i
大,同时j
和k
之间存在j > k
的关系。由于我们的遍历是单向的,因此我们可以将问题转化为找k
,首先k
需要比i
大,同时在[i, k]
之间存在比k
大的数即可。枚举
j
:由于j
是 132 结构里最大的数,因此我们需要在j
的右边中比j
小的「最大」的数,在j
的左边找比j
小的「最小」的数。这很容易联想到单调栈,但是朴素的单调栈是帮助我们找到左边或者右边「最近」的数,无法直接满足我们「最大」和「最小」的要求,需要引入额外逻辑。枚举
k
:由于k
是 132 结构中的中间值,这里的分析逻辑和「枚举 i」类似,因为遍历是单向的,我们需要找到k
左边的i
,同时确保[i,k]
之间存在比i
和k
大的数字。
以上三种分析方法都是可行的,但「枚举 i」的做法是最简单的。
因为如果存在 (j,k)
满足要求的话,我们只需要找到一个最大的满足条件的 k
,通过与 i
的比较即可。
也许你还不理解是什么意思。没关系,我们一边证明一边说。
过程 & 证明
先说处理过程吧,我们从后往前做,维护一个「单调递减」的栈,同时使用 k
记录所有出栈元素的最大值(k
代表满足 132 结构中的 2)。
那么当我们遍历到 i
,只要满足发现满足 nums[i] < k
,说明我们找到了符合条件的 i j k
。
举个🌰,对于样例数据 [3, 1, 4, 2]
,我们知道满足 132 结构的子序列是 [1, 4, 2]
,其处理逻辑是(遍历从后往前):
- 枚举到 2:栈内元素为 [2],
k
= INF - 枚举到 4:不满足「单调递减」,2 出栈更新
k
,4 入栈。栈内元素为 [4],k
= 2 - 枚举到 1:满足
nums[i] < k
,说明对于i
而言,后面有一个比其大的元素(满足i < k
的条件),同时这个k
的来源又是因为维护「单调递减」而弹出导致被更新的(满足i
和k
之间,有比k
要大的元素)。因此我们找到了满足 132 结构的组合。
这样做的本质是:我们通过维护「单调递减」来确保已经找到了有效的 (j,k)
。换句话说如果 k
有值的话,那么必然是因为有 j > k
,导致的有值。也就是 132 结构中,我们找到了 32,剩下的 i
(也就是 132 结构中的 1)则是通过遍历过程中与 k
的比较来找到。这样做的复杂度是 $O(n)$ 的,比树状数组还要快。
从过程上分析,是没有问题的。
搞清楚了处理过程,证明也变得十分简单。
我们不失一般性的考虑任意数组 nums
,假如真实存在 ijk
符合 132 的结构(这里的 ijk
特指所有满足 132 结构要求的组合中 k
最大的那个组合)。
由于我们的比较逻辑只针对 i
和 k
,而 i
是从后往前的处理的,必然会被遍历到;漏掉 ijk
的情况只能是:在遍历到 i
的时候,我们没有将 k
更新到变量中:
- 这时候变量的值要比真实情况下的
k
要小,说明k
还在栈中,而遍历位置已经到达了i
,说明j
和k
同时在栈中,与「单调递减」的性质冲突。 - 这时候变量的值要比真实情况下的
k
要大,说明在k
出栈之后,有比k
更大的数值出栈了(同时必然有比变量更大的值在栈中),这时候要么与我们假设ijk
是k
最大的组合冲突;要么与我们遍历到的位置为i
冲突。
综上,由于「单调递减」的性质,我们至少能找到「遍历过程中」所有符合条件的 ijk
中 k
最大的那个组合。
单调栈
代码(感谢 @宫水三叶的小迷妹 同学和 @🍭可乐可乐吗QAQ 同学 提供的 python & cpp 版本):
class Solution {
public boolean find132pattern(int[] nums) {
int n = nums.length;
Deque<Integer> d = new ArrayDeque<>();
int k = Integer.MIN_VALUE;
for (int i = n - 1; i >= 0; i--) {
if (nums[i] < k) return true;
while (!d.isEmpty() && d.peekLast() < nums[i]) {
// 事实上,k 的变化也具有单调性,直接使用 k = pollLast() 也是可以的
k = Math.max(k, d.pollLast());
}
d.addLast(nums[i]);
}
return false;
}
}
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
stack = []
k = -(10 ** 9 + 7)
for i in range(len(nums) - 1,-1,-1):
if nums[i] < k:
return True
while stack and stack[-1] < nums[i]:
k = max(k,stack.pop())
stack.append(nums[i])
return False
class Solution {
public:
bool find132pattern(vector<int>& nums) {
stack<int> st;
int n = nums.size(), k = INT_MIN;
for(int i = n - 1; i >= 0; i--){
if(nums[i] < k) return true;
while(!st.empty() and st.top() < nums[i]) {
k = max(k,st.top()); st.pop();
}
st.push(nums[i]);
}
return false;
}
};
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
最后
如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ (“▔□▔)/
如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~
也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
58862 | 162307 | 36.3% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|