给定一个整数序列:a1, a2, …, an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。
注意:n 的值小于15000。
示例1:
输入: [1, 2, 3, 4]
输出: False
解释: 序列中不存在132模式的子序列。
示例 2:
输入: [3, 1, 4, 2]
输出: True
解释: 序列中有 1 个132模式的子序列: [1, 4, 2].
示例 3:
输入: [-1, 3, 2, 0]
输出: True
解释: 序列中有 3 个132模式的的子序列: [-1, 3, 2], [-1, 3, 0] 和 [-1, 2, 0].
思路:
首先想到的是三层循环暴力解,不过这肯定超时。
1.我们可以固定最大的aj,然后就可以确定最小的ai,ai取序列a1…aj的最小值,这样可以使ak的取值范围更大,但是必须满足nums[j]>min[j], min[j]代表ai取序列a1…aj的最小值。
2.aj从右往左遍历,aj和ai固定好了,接下来就确定ak,ak需要满足的条件是:ak>mid[j],ak的取值在j后面的数。
3.如果每次固定了aj和ai后都一一比较ak>ai,复杂度很高。所以我们引入了栈来优化。因为aj从右往左,ak必须是满足了nums[j]>min[j]的nums[j],所以我们用栈来存储nums[j],用于ak的取值。
代码:
class Solution {
public boolean find132pattern(int[] nums) {
if (nums.length < 3) {
return false;
}
//计算0--j的最小值
int[] min = new int[nums.length];
min[0] = nums[0];
//min[j]代表的是0-j的最小值
for (int i = 1; i < nums.length; i++) {
min[i] = Math.min(min[i-1],nums[i]);
}
Stack<Integer> stack = new Stack<>();
//从右完左遍历j
for (int j = nums.length - 1; j >0; j--) {
if (nums[j] > min[j]) {
//比当前的最小值要小出栈,
while(!stack.isEmpty() && stack.peek() < min[j]) {
stack.pop();
}
//栈顶元素不为空,说明大于ak大于最小值。只要在满足小于nums[j]则满足条件
if (!stack.isEmpty() && stack.peek() < nums[j]) {
return true;
}
//进栈
stack.push(nums[j]);
}
}
return false;
}
}