leetcode . 132 模式(单调栈-java)

文章介绍了如何使用单调栈解决LeetCode中的132模式问题,通过遍历数组并维护一个单调递减的栈,找到满足132模式的子序列。在给定的示例中,通过具体操作解释了算法的逻辑,并证明了这种方法的正确性。

leetcode456 . 132 模式

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

题目介绍

给你一个整数数组 nums ,数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]、nums[j] 和 nums[k] 组成,并同时满足:i < j < k 和 nums[i] < nums[k] < nums[j] 。
如果 nums 中存在 132 模式的子序列 ,返回 true ;否则,返回 false 。

示例 1:
输入:nums = [1,2,3,4]
输出:false
解释:序列中不存在 132 模式的子序列。

示例 2:
输入:nums = [3,1,4,2]
输出:true
解释:序列中有 1 个 132 模式的子序列: [1, 4, 2] 。

示例 3:
输入:nums = [-1,3,2,0]
输出:true
解释:序列中有 3 个 132 模式的的子序列:[-1, 3, 2]、[-1, 3, 0] 和 [-1, 2, 0]

单调栈

我们从后往前做,维护一个「单调递减」的栈,同时使用 k 记录所有出栈元素的最大值(k 代表满足 132 结构中的 2)。

那么当我们遍历到 i,只要满足发现满足 nums[i] < k,说明我们找到了符合条件的 i j k。

举个例子,对于样例数据 [3, 1, 4, 2],我们知道满足 132 结构的子序列是 [1, 4, 2],其处理逻辑是(遍历从后往前):

枚举到 2:栈内元素为 [2],k = INF
枚举到 4:不满足「单调递减」,2 出栈更新 k,4 入栈。栈内元素为 [4],k = 2
枚举到 1:满足 nums[i] < k,说明对于 i 而言,后面有一个比其大的元素(满足 i < k 的条件),同时这个 k 的来源又是因为维护「单调递减」而弹出导致被更新的(满足 i 和 k 之间,有比 k 要大的元素)。因此我们找到了满足 132 结构的组合。
这样做的本质是:我们通过维护「单调递减」来确保已经找到了有效的 (j,k)。换句话说如果 k 有值的话,那么必然是因为有 j > k,导致的有值。也就是 132 结构中,我们找到了 32,剩下的 i (也就是 132 结构中的 1)则是通过遍历过程中与 k 的比较来找到。这样做的复杂度是 的,比树状数组还要快。

从过程上分析,是没有问题的。
搞清楚了处理过程,证明也变得十分简单。

我们不失一般性的考虑任意数组 nums,假如真实存在 ijk 符合 132 的结构(这里的 ijk 特指所有满足 132 结构要求的组合中 k 最大的那个组合)。

由于我们的比较逻辑只针对 i 和 k,而 i 是从后往前的处理的,必然会被遍历到;漏掉 ijk 的情况只能是:在遍历到 i 的时候,我们没有将 k 更新到变量中:

这时候变量的值要比真实情况下的 k 要小,说明 k 还在栈中,而遍历位置已经到达了 i,说明 j 和 k 同时在栈中,与「单调递减」的性质冲突。
这时候变量的值要比真实情况下的 k 要大,说明在 k 出栈之后,有比 k 更大的数值出栈了(同时必然有比变量更大的值在栈中),这时候要么与我们假设 ijk 是 k 最大的组合冲突;要么与我们遍历到的位置为 i 冲突。
综上,由于「单调递减」的性质,我们至少能找到「遍历过程中」所有符合条件的 ijk 中 k 最大的那个组合。

  public boolean find132pattern(int[] nums) {
        if (nums.length < 3){
            return false;
        }
        int n = nums.length;
        Deque<Integer> d = new ArrayDeque<>();
        int num = Integer.MIN_VALUE;
        for (int i = n - 1;i >= 0;i--){
            if (nums[i] < num){
                return true;
            }
            while (!d.isEmpty() && d.peekLast() < nums[i]){
                num = Math.max(num,d.peekLast());
            }
            d.addLast(nums[i]);
        }

        return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值