难度中等
给你一个整数数组 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 * 10^5
-10^9 <= nums[i] <= 10^9
# 枚举 1
由于是 132
模式,即:
枚举下标 ,找到大于 的元素下标 ,然后只需要在 范围内找到大于 的元素即可
由于 32
都在 1
的右边,所以应该倒序枚举
因为需要预先知道
1
后面的所有值
可以维护单调递减栈:顶 [0,3,4] 底
若 ,则不断地弹出栈顶直至 ,而弹出的最后一个元素即为 2
小于 3
的最大元素 nums[k]
此时栈顶元素即为
3
,弹出的元素即为2
,必然有2
小于3
而 即为
1
,只需要检查1
是否小于2
即可,即:
例如:
。压入
。压入
,则弹出 并更新最小元素 为 ,
,则弹出 并更新最小元素 为 ,
压入
- ,满足条件,返回
class Solution { | |
public boolean find132pattern(int[] nums) { | |
int n = nums.length; | |
// 递减栈 | |
Deque<Integer> st = new ArrayDeque<>(); | |
int k = Integer.MIN_VALUE; | |
for (int i = n - 1; i >= 0; i--) { | |
// 此时已然满足 `2` < `3`, 只需看 `1` < `2` ? | |
if (nums[i] < k) { | |
return true; | |
} | |
// 弹出栈顶元素直至 peek >= nums [i] | |
while (!st.isEmpty() && st.peekFirst() < nums[i]) { | |
k = st.pollFirst(); | |
} | |
st.addFirst(nums[i]); | |
} | |
return false; | |
} | |
} |
# 枚举 2
维护单调递减栈:底 [3,2,1] 顶。栈顶元素即为 3
,枚举 的同时需要尽可能地保证 3
大
若采用递增栈,在枚举到 的时候,,栈中只有 ,但需要的是
若 ,不断地弹出栈顶元素直至 。此时栈顶元素即为 3
,只需要寻找 3
左边比 3
小的最小元素 1
并判断 1
是否满足 1 < 2
即可。
在枚举 的同时维护 左边比 小的最小元素
1
,因为 必然在 左边。
为什么此时栈顶元素即为 3
,而不是栈顶下面的元素?,况且栈顶下面的元素更大于栈顶元素
若栈顶元素
3
不满足条件l[peek] < nums[k]
,由于是递减栈,栈中元素必然大于等于栈顶元素, 必然小于等于栈顶元素,所以 也必然小于等于栈中元素况且栈顶元素都不满足条件,即: ,而栈中元素 ,所以 ,所以栈中元素也不会满足条件。
例如
- ,不满足条件
,也必然不可能满足条件
class Solution { | |
public boolean find132pattern(int[] nums) { | |
int n = nums.length; | |
// 递减栈 | |
Deque<Integer> st = new ArrayDeque<>(); | |
//j 左边满足小于 nums [j] 的最小元素 | |
int[] l = new int[n]; | |
int mn = Integer.MAX_VALUE; | |
// 枚举 k | |
for (int k = 0; k < n; k++) { | |
while (!st.isEmpty() && nums[st.peekFirst()] <= nums[k]) { | |
st.pollFirst(); | |
} | |
// 此时栈顶元素就是 `3` | |
if (!st.isEmpty() && l[st.peekFirst()] < nums[k]) { | |
return true; | |
} | |
l[k] = mn; | |
mn = Math.min(mn, nums[k]); | |
st.addFirst(k); | |
} | |
return false; | |
} | |
} |
# 枚举 3
# 单调栈
枚举下标 ,找到小于 的元素下标 ,找到大于 的元素下标 ,需要满足
我们可以最小化 ,最大化
即: 左边小于 的最小元素 , 右边大于 的最大元素
-
对于 ,可以预处理,计算 的最小元素 即可
-
对于 ,可以维护一个自底向上的递减栈:顶 底。从后往前遍历
若 ,则不断地弹出栈顶元素直至 。而弹出的最后一个栈顶元素即为「右边大于 的最大元素 」
最后比较 是否小于 即可
为
3
,弹出的最后一个元素为2
,必然满足3 > 2
,此时只需要看1 < 2 ?
,而1
即为例如:
,压入
,压入
-
,则弹出 并更新最小元素为 ,
,则弹出 并更新最小元素为 ,
比较 与 的大小
-
class Solution { | |
public boolean find132pattern(int[] nums) { | |
int n = nums.length; | |
//l : 左边小于 nums [i] 的最小元素 | |
int[] l = new int[n]; | |
l[0] = Integer.MAX_VALUE; | |
int mn = nums[0]; | |
for (int i = 1; i < n; i++) { | |
l[i] = mn; | |
mn = Math.min(nums[i], mn); | |
} | |
// 递减栈 | |
Deque<Integer> st = new ArrayDeque<>(); | |
for (int j = n - 1; j >= 0; j--) { | |
int k = -1; | |
// 不断地弹栈直至栈顶元素大于 nums [j] | |
while (!st.isEmpty() && nums[st.peekFirst()] < nums[j]) { | |
k = st.pollFirst(); | |
} | |
if (k != -1 && l[j] < nums[k]) { | |
return true; | |
} | |
st.addFirst(j); | |
} | |
return false; | |
} | |
} |
# 平衡树
枚举下标 ,找到小于 的元素下标 ,找到大于 的元素下标 ,需要满足
我们可以最小化 ,最大化
即: 左边小于 的最小元素 , 右边大于 的最大元素
-
对于 ,可以预处理,计算 的最小元素 即可
-
对于 ,可以在倒序枚举 的同时维护 的升序集合,二分查询大于等于
1
的最小元素2
,然后比较2
与3
的大小即可其实也可以二分查询小于等于
3
的最大元素2
,然后比较1
与2
的大小
class Solution { | |
public boolean find132pattern(int[] nums) { | |
int n = nums.length; | |
//l : 左边小于 nums [i] 的最小元素 | |
int[] l = new int[n]; | |
l[0] = Integer.MAX_VALUE - 1; | |
int mn = nums[0]; | |
for (int i = 1; i < n; i++) { | |
l[i] = mn; | |
mn = Math.min(nums[i], mn); | |
} | |
// 升序集合 | |
TreeSet<Integer> set = new TreeSet<>(); | |
for (int j = n - 1; j >= 0; j--) { | |
// 二分查询大于等于 `1` 的最小元素 `2` | |
Integer k = set.ceiling(l[j] + 1); | |
if (k != null && k < nums[j]) { | |
return true; | |
} | |
set.add(nums[j]); | |
} | |
return false; | |
} | |
} |