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;
}

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

被折叠的 条评论
为什么被折叠?



