LeetCode——456.132模式

给定一个整数序列: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].

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/132-pattern

这道题给我们了一个数组,让我们找到 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;
    }
};    

下面这种方法利用单调栈来做,既简洁又高效,关于单调栈可以参见博主之前的一篇文章 LeetCode Monotonous Stack Summary 单调栈小结。思路是我们维护一个栈和一个变量 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 值小这个条件,举个例子来说吧,比如 [2, 4, 2, 3, 5],由于是从后往前遍历,所以后三个数都不会进入 while 循环,那么栈中的数字为 5, 3, 2(其中2为栈顶元素),此时 third 还是整型最小,那么当遍历到4的时候,终于4大于栈顶元素2了,那么 third 赋值为2,且2出栈。此时继续 while 循环,因为4还是大于新栈顶元素3,此时 third 赋值为3,且3出栈。现在栈顶元素是5,那么 while 循环结束,将4压入栈。下一个数字2,小于 third,则找到符合要求的序列 [2, 4, 3],参见代码如下:

解法三:

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,是可以正确的返回那三个结果的,别的也没怎么试过,感觉应该是正确的。

方法一:栈

我们首先考虑 a[i] < a[j] 的部分。当我们固定了 j 时,我可以在 j 的左侧找出一个最小的数作为 a[i],这是因为最终我们需要满足 a[i] < a[k] < a[j],那么 a[i] 一定越小越好。因此我们可以对数组 a 维护前缀最小值,即 min[j] = min(a[1 .. j]),这样对于一个固定的 j,min[j] 即为最优的 a[i]。

随后我们再考虑 a[k],其中 a[k] 需要满足 a[i] < a[k] < a[j],即 min[j] < a[k] < a[j]。我们可以从数组 a 的末尾开始,从后向前寻找 a[k]。

我们可以用栈来存储所有的 a[k]。在栈中,所有候选的 a[k] 保持降序,即栈顶的元素最小,栈底的元素最大。即如果我们遇到一个新的 a[k],那么我们会将栈顶的元素依次出栈,直到新的 a[k] 为栈中的最小元素。我们在从右向左遍历数组 a 时,假设我们当前位于 a[j],首先我们判断是否有 nums[j] > min[j],如果不成立,那么我们要跳过这个 a[j],否则我们将栈顶的元素依次出栈,直到栈顶元素 stack[top] 满足 stack[top] > min[j]。在这之后,我们可以确定栈中的所有元素都大于 min[j](即 num[i]),因此如果此时栈顶的元素和 j 可以满足 132 模式,那么我们就找到了一组合法的满足 132 模式的 i, j, k,否则我们继续寻找,此时需要把 a[j] 入栈。

如果在遍历结束后,我们仍然没有找到满足 132 模式的 i, j, k,那么我们需要返回 False。

Java

public class Solution {
    public boolean find132pattern(int[] nums) {
        if (nums.length < 3)
            return false;
        Stack < Integer > stack = new Stack < > ();
        int[] min = new int[nums.length];
        min[0] = nums[0];
        for (int i = 1; i < nums.length; i++)
            min[i] = Math.min(min[i - 1], nums[i]);
        for (int j = nums.length - 1; j >= 0; j--) {
            if (nums[j] > min[j]) {
                while (!stack.isEmpty() && stack.peek() <= min[j])
                    stack.pop();
                if (!stack.isEmpty() && stack.peek() < nums[j])
                    return true;
                stack.push(nums[j]);
            }
        }
        return false;
    }
}

复杂度分析

时间复杂度:O(N)。

空间复杂度:O(N)。

M1: 栈

问题是在一个整数序列中,有没有一个子序列符合 ai < ak < aj, 其中 i<j<k

所以可以把问题转化成,找到一个元素 aj, 在区间[1, j-1]里有比他小的元素M1,在区间[j+1, n]里也有比他小的元素M2, 并且M2>M1

所以这里需要让M1尽可能小,所以第一步就是维护一个最小前缀值的数组,即aj对应的最小M1

mi = [nums[0]]
for i in range(1, le):
    mi.append(min(nums[i], mi[-1]))

因为有了前缀最小值,所以我们可以很快判断aj和M1的关系,接下来的任务就是找M2。
首先可以想到暴力解,遍历[j+1]到n的每一个数,时间复杂度是O(n^2),那么有什么办法可以优化这部分,我们可以从数组尾部开始向前维护一个单调递减栈,对于每一个aj,如果aj>M1,然后在当前栈中找比M1大的最小值,即以M1为最小标准值来维护这个递减栈之后的栈顶元素,如果栈顶元素小于aj,即找到我们所需要的情况

stack = []
for i in range(le-1, -1, -1):
    if nums[i]>mi[i]:
        while stack and mi[i]>=stack[-1]:
            stack.pop()

        if stack and stack[-1]<nums[i]:
            return True
        stack.append(nums[i])

为什么以M1为最小值维护栈不会对后续造成影响

while stack and mi[i]>=stack[-1]:
    stack.pop()

我们先看看最小前缀值数组有什么特征,很容易得出它是一个非递增数组,
后面的元素都小于或等于当前元素,所以如果当前栈里的元素小于 aj 对应的M1,那么肯定也小于a[j-1] 到a[1]对应的M1,所以直接出栈即可。

为什么最后可以直接入栈

stack.append(nums[i])

在上一步以M1维护栈之后,栈里的元素都是大于M1,此时如果栈顶的最小值小于nums[i],就已经找到我们要的情况,否则的话栈内元素都是大于nums[i]的,直接把nums[i]推进栈,不影响递减。

完整代码

class Solution:
    def find132pattern(self, nums: List[int]) -> bool:
        le = len(nums)
        if le<2: return False

        mi = [nums[0]]
        for i in range(1, le):
            mi.append(min(nums[i], mi[-1]))

        stack = []
        for i in range(le-1, -1, -1):
            print(stack)
            if nums[i]>mi[i]:
                while stack and mi[i]>=stack[-1]:
                    stack.pop()

                if stack and stack[-1]<nums[i]:
                    return True
                stack.append(nums[i])
        return False

原文地址:https://www.cnblogs.com/wwj99/p/12307534.html

时间: 2024-11-05 11:06:40

LeetCode——456.132模式的相关文章

Leetcode 456.132模式

132模式 给定一个整数序列: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 个

Leetcode 456. 132 Pattern

题目的意思就是给你一个数组 里面一堆数,你是否能找到这样三个数,第一个数比第三个数小,第二个数最大.比如 1, 3, 2 或者  4, 9, 5 这种,数字可以不连续在一起,但是要保证顺序不变,只要有这么一组数就可以. 这个题我做了一个小时没想到比较好的办法,去看了别人的思路,看懂以后回来自己实现的,不得不说,想法真的优秀. 思路是这样的,既然是三个数比较,我找到中间那个数就是 2 那个数,再找到一个比2大的, 再找到一个比2 小的,这样就存在这么一组数了.那如何能保证顺序没问题呢,从数组的后面

[Swift]LeetCode456. 132模式 | 32 Pattern

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.

[Leetcode456] 132模式 峰谷法/单调栈

题目:https://leetcode-cn.com/problems/132-pattern/ 思路: 如果某个数左边比它小的数的最小值,小于它右边小于它的某个数(不必找最大值),那么这个序列就符合132模式的定义.如下图三点所示. 于是有解法1(峰谷法): 1 public static boolean find132pattern(int[] nums) { 2 int min = Integer.MAX_VALUE; 3 int max = Integer.MIN_VALUE; 4 bo

[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.

456. 132 Pattern

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.

LC 456. 132 Pattern

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.

Leetcode 290. 单词模式

class Solution { public: bool wordPattern(string pattern, string str) { map<string, char> word_map; char used[128] = {0}; string word; int pos= 0; str.push_back(' '); for(int i=0; i<str.size(); ++i) { if(str[i] == ' ') { //结束条件 if(pos == pattern.

132模式

2018-09-27 23:20:20 问题描述: 问题求解: 本题的难度还是有的,主要的考虑方向是尽量构造[min, max]来将后面出现的数字插入到其中.这里的求解方法是使用Stack来维护一组non-overlapping的区间,每次判断当前的数字能够加入到区间之中,如果可以,那么就直接返回true,如果不可以,那么就需要维护这个区间栈.这里有个地方需要注意到的是在栈顶的元素的左边界是到当前的为止的最小值,换句话说,Stack中的区间是按照左边界进行排序的. public boolean