456. 132 模式
给你一个整数数组 nums
,数组中共有 n
个整数。132 模式的子序列 由三个整数 nums[i]
、nums[j]
和 nums[k]
组成,并同时满足:i < j < k
和 nums[i] < nums[k] < nums[j]
。
如果 nums
中存在 132 模式的子序列 ,返回 true
;否则,返回 false
。
**进阶:**很容易想到时间复杂度为 O(n^2)
的解决方案,你可以设计一个时间复杂度为 O(n logn)
或 O(n)
的解决方案吗?
示例 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] 。
枚举1
如果我们从左到右枚举 1 的下标 i,那么 j,k 的下标范围都是减少的,这样就不利于对它们进行维护。因此我们可以考虑从右到左枚举 i。
我们用单调栈维护所有可以作为 2 的候选元素。初始时,单调栈中只有唯一的元素 a[n−1]。我们还需要使用一个变量 maxK记录所有可以真正作为 2 的元素的最大值;
随后我们从 n−2开始从右到左枚举元素 a[i]
首先我们判断 a[i]是否可以作为 1。如果 a[i]<maxK,那么它就可以作为 1,我们就找到了一组满足 132模式的三元组;
随后我们判断 a[i] 是否可以作为 3,以此找出哪些可以真正作为 2 的元素。我们将 a[i] 不断地与单调栈栈顶的元素进行比较,如果 a[i] 较大,那么栈顶元素可以真正作为 2,将其弹出并更新 maxK;
最后我们将 a[i] 作为 2 的候选元素放入单调栈中。这里可以进行一个优化,即如果 a[i]≤maxK,那么我们也没有必要将 a[i] 放入栈中,因为即使它在未来被弹出,也不会将 maxK 更新为更大的值。
class Solution {
public boolean find132pattern(int[] nums) {
//记录数组长度
int n = nums.length;
//定义单调栈 在单调栈中,从栈底到栈顶的元素是严格单调递减的
//使用单调栈维护2
Deque<Integer> candidateK = new LinkedList<Integer>();
//首先将a[n-1]入栈
candidateK.push(nums[n - 1]);
//将maxK初始化为最下值
int maxK = Integer.MIN_VALUE;
for (int i = n - 2; i >= 0; --i) {
//第一轮进不来 ,进入选择时栈中最少有两个元素
//找1
if (nums[i] < maxK) {
return true;
}
//如果栈不空,且nums[i]>栈中的最大元素,此时就认为当前栈顶作为2,就将栈清空
//然后在下面的选择中将当前元素维护为3
//之后的任务就是找1
while (!candidateK.isEmpty() && nums[i] > candidateK.peek()) {
maxK = candidateK.pop();
}
if (nums[i] > maxK) {
candidateK.push(nums[i]);
}
}
return false;
}
}
单调栈维持1,3
class Solution {
public boolean find132pattern(int[] nums) {
//长度小于3,不符合直接返回false
if(nums.length < 3){
return false;
}
//定义smallest初值为最大值
int smallest = Integer.MAX_VALUE;
//定义单调栈 在单调栈中,从栈底到栈顶的元素是严格单调递减的
//使用单调栈维护着1,3
Deque<int[]> stack = new LinkedList<>();
//遍历数组
for(int num : nums){
//第一轮就进来了 ,始终保持着当前元素比前面的最小元素大
//而smallest维护着此次循环之前的最小元素,作为1
if(num <= smallest){
smallest = num;
continue;
}
//栈非空 遍历栈
while(!stack.isEmpty()){
//获取栈顶
int[] arr = stack.peek();
//如果num>栈中维持的num(也就是3),此时栈中维持的数据已经不适合了,直接出栈
if(num >= arr[1]){
stack.pop();
continue;
}
//如果num<栈中维持的num(也就是3),且num<smallest(也就是最小值) 此时达成 1 3 2
if(arr[0] < num){
return true;
}
//如果本次栈顶的元素smallest>num则继续进行遍历获取最小值
if(arr[0] >= num){
break;
}
}
//将此次循环之前的最小值和一个大于最小值的数入栈
//维护1,3
stack.push(new int[]{smallest, num});
}
return false;
}
}