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模式的子序列。

分析

方法1:直接使用三层循环进行暴力求解。但是会超时。

class Solution {
public:
    bool find132pattern(vector<int>& nums) {
        bool res = false;
        //使用双指针的方式
        for(int i=0;i<nums.size();i++){
           for(int j=i+1;j<nums.size();j++){
               for(int k=j+1;k<nums.size();k++){
                   if(nums[i]<nums[k] && nums[k]<nums[j]) res = true;
               }
           } 
        }
        return res;
    }
};

方法2:对于i, j ,k 考虑每次固定住中间的j索引, 针对每一个j求得nums[:j]的最小值,然后在nums[j:]寻找到合适的k值。

class Solution:
    def find132pattern(self, nums: List[int]) -> bool:
        for j in range(1, len(nums)-1):
            ai = min(nums[:j])# 这一步可以考虑使用以为数组进行事先求解存储
            for ak in nums[j:]:
                if ai<ak<nums[j]: return True
        return False

对于ai的求解,可以考虑使用前缀和的思路进行预先处理,从而降低时间复杂度。

class Solution:
    def find132pattern(self, nums: List[int]) -> bool:
        mav_v = max(nums)
        lis = [mav_v for i in range(len(nums))]
        lis[0] = nums[0]
        for j in range(1, len(nums)-1):
            if(nums[j]<nums[j-1]): lis[j] = nums[j]
            else: lis[j] = lis[j-1]

        for j in range(1, len(nums)-1):
            ai = lis[j]
            for ak in nums[j:]:
                if ai<ak<nums[j]: return True
        return False

按照上面的解法,进行复杂度分析,即时间复杂度为: O ( n − 1 + n − 2 + n − 3 + n − 4 + . . . + n − ( n − 1 ) ) = O ( n 2 ) O(n-1 + n-2 + n-3 + n-4 + ... + n-(n-1)) = O(n^2) O(n1+n2+n3+n4+...+n(n1))=O(n2)

方法3:双指针

class Solution {
    public boolean find132pattern(int[] nums) {
        //就是在i和k之间寻找一个索引j使得,nums[j] > nums[i] > nums[i]
        int i = 0;
        int k = nums.length-1;
        while(i<k) {
            while (i<nums.length && nums[i]>=nums[k]) i++; //先固定住右边指针
            //此时的nums[i] < nums[k], 在i和k之间进行找j
            for(int index=i+1;index<k;index++){
                if (nums[index]>nums[k]) return true;
            } 
            k--;
            i=0;  
        }
        i = 0;
        k = nums.length-1;
        while(i<k) {
            while (k>=0 && nums[k]<=nums[i]) k--; //先固定住左边指针
            //此时的nums[i] < nums[k], 在i和k之间进行找j
            for(int index=i+1;index<k;index++){
                if (nums[index]>nums[k]) return true;
            }   
            i++;
            k = nums.length-1;
        }
        
        return false;
    }
}

方法4:单调栈
考虑使用单调栈从而进行降低时间复杂度,注意在上面的解法2中核心就是在j的后面找到合适的k使得满足条件。对于k的寻找,考虑从右边往左进行寻找。我们进行维持一个递减的栈,

解释一下为啥用递减栈:

  • 我们需要找a[k] < a[j] 且 a[k] > min[j] ,假如不用栈,对于每一个j,我们需要遍历j之后的所有元素来判断是否符合条件, O ( n 2 ) O(n^2) O(n2)复杂度,

  • 因为有min[j-1] >= min[j], 利用递减栈,栈中大于等于min[j] 的元素都出栈了,那么当找下一轮j-1时,上一轮出栈的元素一定也是不符合条件提前出局。这样就可以利用上一轮的结果避免重复判断,将复杂度缩小到 o ( n ) o(n) o(n)

  • 为什么从后往前遍历?因为找k的时候k在j后面,所以得是从后往前遍历,这样栈中元素都是j后面的元素。

  • 为什么是递减栈? 如果是递增栈,那么对于a[k] > min[j], 如果栈顶满足条件,我们还需要检查a[k] < a[j].如果不符合条件,我们需要弹出该元素继续寻找下一个元素。这个被弹出的元素a[k]。因为a[k] > min[j] >= min[j-1]。却因为被弹出栈导致下一轮j-1无法搜索。因此无法用递增栈。

  • 为什么我们找a[k] < a[j] 且 a[k] > min[j] 的时候,是先找大于min[j]的元素,然后再比较该元素与a[j]的关系。而不是反过来,先找小于a[j]的元素,再比较该元素与min[j]的关系? 相信你应该有了答案。

  • 总结:利用单调栈以及上一轮与下一轮的联系(min[j-1] <= min[j])避免了同一元素的重复判断,从而降低时间复杂度。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值