456. 132模式
思路:
扫了一眼题目,感觉需要用到栈,我的想法是用用栈来保存递增序列(栈顶最大),遍历一遍数组,当当前元素比栈顶元素大或等于就入栈,比栈顶小就满足条件啦~
public boolean find132pattern(int[] nums) {
Stack<Integer> stack = new Stack<>();
for(int i=0;i<nums.length;i++){
if(stack.isEmpty() || stack.peek()<=nums[i]){
stack.add(nums[i]);
continue;
}
if(!stack.isEmpty() && stack.size()>1 && nums[i]<stack.peek()){
return true;
}
}
return false;
}
但是这样我发现有漏洞由于我们值存大于栈顶的元素,那么【1,-3,0,-2】这种情况我们就得不出【-3,0,-2】这个子序列,因为-3被限制无法入栈,所以我们再外层加一个循环,这样每个元素都有入栈的机会啦~
class Solution {//错误,我以为满足nums[i]<nums[j]>nums[k]就行了,但是题目要求是nums[i]<nums[k]<nums[j]
public boolean find132pattern(int[] nums) {
for(int j=0;j<nums.length;j++){
Stack<Integer> stack = new Stack<>();
for(int i=j;i<nums.length;i++){
if(stack.isEmpty() || stack.peek()<=nums[i]){
stack.add(nums[i]);
continue;
}
if(!stack.isEmpty() && stack.size()>1 && nums[i]<stack.peek()){
return true;
}
}
}
return false;
}
}
但是这样还是错了!!题目要求是nums[i]<nums[k]<nums[j],而我只满足了nums[i]<nums[j]>nums[k] /(ㄒoㄒ)/~~
不过改改就行啦,只要我们加一个限制条件使nums[i]<nums[k](即nums[i]>nums[j])满足就可以了:
class Solution {//对,但是超时
public boolean find132pattern(int[] nums) {
for(int j=0;j<nums.length;j++){
Stack<Integer> stack = new Stack<>();
for(int i=j;i<nums.length;i++){
if(stack.isEmpty() || stack.peek()<=nums[i]){
stack.add(nums[i]);
continue;
}
if(!stack.isEmpty() && stack.size()>1 && nums[i]<stack.peek() && nums[i]>nums[j]){
return true;
}
}
}
return false;
}
}
这样的确没问题,但是超时了。。。
于是!!我想到之前用回溯做排列组合的时候是去重了的,这里也需要去重呀,比如外层循环中相邻两个数相同那么只需遍历一次即可,加上这个判断if(j>0 && nums[j] == nums[j-1]) continue;就可以啦:
class Solution {//通过
public boolean find132pattern(int[] nums) {
if(nums.length < 3) return false;
for(int j=0;j<nums.length;j++){
if(j>0 && nums[j] == nums[j-1]) continue;
Stack<Integer> stack = new Stack<>();
for(int i=j;i<nums.length;i++){
if(stack.isEmpty() || stack.peek()<=nums[i]){
stack.add(nums[i]);
continue;
}
if(!stack.isEmpty() && stack.size()>1 && nums[i]<stack.peek() && nums[i]>nums[j]){
return true;
}
}
}
return false;
}
}
终于通过了!!但是时间复杂度仍然太高,我们如何才能去掉一层循环呢?我确实没想出来,于是就参考了下别人的答案。
别人的思路是这样的,首先是倒着遍历,因为如果我们固定最后两个数找第一个数会比较方便,因为第一个数只需满足比第三个数小即可。
好,接下来我们还是需要用到栈,栈中保存一个递减序列(栈顶最小),也就是只要小于等于栈顶的元素都是直接入栈,如果遇到比栈顶大的元素,那就意味着我们找到了第二个数,也就是最大的那个数,那呢第三个数一定在栈中!(因为第三个数索引比第二个数大,我们是倒着遍历的)那么我们选栈中哪个数作为第三个数呢?我们用栈底的数,因为栈底的数最大,选一个大的数能给第一个数留下更多的选择空间,那么我们将这个第三个数记录为last(栈要清空),每再遍历一个数小于last就可以返回true啦:
class Solution {
public boolean find132pattern(int[] nums) {
int n = nums.length;
int last = Integer.MIN_VALUE; // 132中的2
Stack<Integer> sta = new Stack<>();// 用来存储132中的3
if(nums.length < 3) return false;
for(int i=n-1; i>=0; i--){
if(nums[i] < last) // 若出现132中的1则返回正确值,此时能保证132中的1:num[i]在最前面(因为是从后往前遍历)
return true;
// 若当前值大于或等于2则更新2(2为栈中小于当前值的最大元素)
while(!sta.isEmpty() && sta.peek() < nums[i]){//若栈中的元素一直小于num[i]则栈会被清空并最后加入最大的那个元素
last = sta.pop();//pop出来的都是之前遍历过的数,所以能保证132中2在3的后面出现(因为数组从后面开始遍历的)(这是pop出来的是比nums[i]小的一个最大的数,因为last最终等于栈底)
}
// 将当前值压入栈中
sta.push(nums[i]);
}
return false;
}
}