[LeetCode] 132 Pattern 132模式

 

Given a sequence of n integers a1, a2, …, an, a 132 pattern is a subsequence ai, aj, ak such that i < j < k and ai < ak < aj. Design an algorithm that takes a list of n numbers as input and checks whether there is a 132 pattern in the list.

Note: n will be less than 15,000.

Example 1:

Input: [1, 2, 3, 4]

Output: False

Explanation: There is no 132 pattern in the sequence.

 

Example 2:

Input: [3, 1, 4, 2]

Output: True

Explanation: There is a 132 pattern in the sequence: [1, 4, 2].

 

Example 3:

Input: [-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].

 

这道题给我们了一个数组,让我们找到132的模式,就是第一个数小于第二第三个数,且第三个数小于第二个数。当然最直接最暴力的方法,就是遍历所有的三个数字的组合,然后验证是否满足这个规律。得莫,OJ说打妹。那么就只能想办法去优化了,由于暴力搜索的时间复杂度是三次方,在之前的 3Sum 那道题中,我们也有把立方的复杂度减少到平方的复杂度,相当于降了一维(降维打击么?),其实就是先固定一个数字,然后去遍历另外两个数字。我们先确定哪个数字呢,当然是最小的那个啦,我们维护一个变量mn,初始化为整型最大值,然后在遍历数字的时候,每次用当前数字来更新mn,然后我们判断,若mn为当前数字就跳过,因为需要找到数字j的位置,数字j是大于数字i的,mn表示的就是数字i。这样数字i和数字j都确定了之后,就要来遍历数字k了,范围是从数组的最后一个位置到数字j之间,只要中间的任何一个数字满足题目要求的关系,就直接返回true即可,参见代码如下:

 

解法一:

class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int n = nums.size(), mn = INT_MAX;
        for (int j = 0; j < n; ++j) {
            mn = min(mn, nums[j]);
            if (mn == nums[j]) continue;
            for (int k = n - 1; k > j; --k) {
                if (mn < nums[k] && nums[j] > nums[k]) return true;
            }
        }
        return false;
    }
};

 

那么我们就按顺序来找这三个数,首先我们来找第一个数,这个数需要最小,那么我们如果发现当前数字大于等于后面一个数字,我们就往下继续遍历,直到当前数字小于下一个数字停止。然后我们找第二个数字,这个数字需要最大,那么如果我们发现当前数字小于等于下一个数字就继续遍历,直到当前数字大雨下一个数字停止。最后就找第三个数字,我们验证这个数字是否在之前两个数字的中间,如果没有找到,我们就从第二个数字的后面一个位置继续开始重新找这三个数字,参见代码如下:

 

解法二:

class Solution {
public:
    bool find132pattern(vector<int>& nums) {int n = nums.size(), i = 0, j = 0, k = 0;
        while (i < n) {
            while (i < n - 1 && nums[i] >= nums[i + 1]) ++i;
            j = i + 1;
            while (j < n - 1 && nums[j] <= nums[j + 1]) ++j;
            k = j + 1;
            while (k < n) {
                if (nums[k] > nums[i] && nums[k] < nums[j]) return true;
                ++k;
            }
            i = j + 1;
        }
        return false;
    }
};    

 

下面这种方法利用来栈来做,既简洁又高效,思路是我们维护一个栈和一个变量third,其中third就是第三个数字,也是pattern 132中的2,栈里面按顺序放所有大于third的数字,也是pattern 132中的3,那么我们在遍历的时候,如果当前数字小于third,即pattern 132中的1找到了,我们直接返回true即可,因为已经找到了,注意我们应该从后往前遍历数组。如果当前数字大于栈顶元素,那么我们按顺序将栈顶数字取出,赋值给third,然后将该数字压入栈,这样保证了栈里的元素仍然都是大于third的,我们想要的顺序依旧存在,进一步来说,栈里存放的都是可以维持second > third的second值,其中的任何一个值都是大于当前的third值,如果有更大的值进来,那就等于形成了一个更优的second > third的这样一个组合,并且这时弹出的third值比以前的third值更大,为什么要保证third值更大,因为这样才可以更容易的满足当前的值first比third值小这个条件,参见代码如下:

 

解法三:

class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        int third = INT_MIN;
        stack<int> st;
        for (int i = nums.size() - 1; i >= 0; --i) {
            if (nums[i] < third) return true;
            while (!st.empty() && nums[i] > st.top()) {
                third = st.top(); st.pop();
            }
            st.push(nums[i]);
        }
        return false;
    }
};

 

讨论:这道题的一个很好的Follow up就是求出所有132模式的数组,那么解法二和解法三这种想要快速来验证是否存在132模式的方法就不太适合,而解法一就比较适合了,我们是需要在找到的时候不直接return,而是将mn,数字j,和数字k,放到一个数组里,然后加入结果res中,试了题目中的例子3,是可以正确的返回那三个结果的,别的也没怎么试过,感觉应该是正确的。

 

参考资料:

https://leetcode.com/problems/132-pattern/

https://leetcode.com/problems/132-pattern/discuss/94135/c_ac

https://leetcode.com/problems/132-pattern/discuss/94133/Simple-java-accepted-well-explained-O(n2)-solution

https://leetcode.com/problems/132-pattern/discuss/94071/single-pass-c-on-space-and-time-solution-8-lines-with-detailed-explanation

https://leetcode.com/problems/132-pattern/discuss/94089/Java-solutions-from-O(n3)-to-O(n)-for-%22132%22-pattern-(updated-with-one-pass-slution)

 

    原文作者:Grandyang
    原文地址: http://www.cnblogs.com/grandyang/p/6081984.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞