132模式-LeetCode
456. 132 模式
🎯 问题描述
描述:
给你一个整数数组 nums,数组中共有 n 个整数。
132 模式的子序列由三个整数 nums[i]、nums[j] 和 nums[k] 组成,并同时满足:
i < j < knums[i] < nums[k] < nums[j]
如果 nums 中存在 132 模式的子序列,返回 true;否则,返回 false。
说明:
- 这是一个枚举中间最大值结合有序集合维护后缀的问题。
- 核心策略:
- 我们要找 。
- 我们可以枚举中间的元素
nums[j]作为“3”(最大值)。 - 左侧 (
1):为了让 更容易满足,对于固定的j,我们需要知道j左侧的最小值min_left。 - 右侧 (
2):我们需要在j的右侧找到一个数nums[k],使得min_left < nums[k] < nums[j]。
- 数据结构:
- 左侧最小值可以通过变量动态维护。
- 右侧的数可以使用一个有序列表来维护,利用二分查找快速找到大于
min_left的最小值。
- 示例:
- 示例 1:
1 | 输入:nums = [3, 1, 4, 2] |
💻 解题思路
思路1:枚举中间点 + 有序后缀集合
核心逻辑
- 初始化:
pre:记录nums[0...j-1]的最小值,初始为nums[0]。suf:一个有序列表,初始包含nums[2...n-1]的所有元素。这是因为j从 1 开始,右侧至少要有k。
- 枚举中间点
j:- 遍历
j从1到n-2。 - 查找目标
k:- 我们需要在
suf中找到一个数nums[k],满足pre < nums[k] < nums[j]。 - 利用二分查找
bisect_right(suf, pre)找到第一个严格大于pre的元素的位置idx。 - 如果
idx合法(即存在这样的元素),检查该元素suf[idx]是否小于nums[j]。 - 如果满足
suf[idx] < nums[j],说明找到了 132 模式,返回True。
- 我们需要在
- 更新状态:
- 更新左侧最小值:
pre = min(pre, nums[j])。 - 更新右侧集合:将即将成为下一个
j的当前nums[j+1]从suf中移除(因为下一轮循环中,j会右移,原来的j+1变成了中间的j,不再是右侧的k)。
- 更新左侧最小值:
- 遍历
代码实现
1 | import bisect |
📊 性能分析
- 时间复杂度:
- 外层循环遍历
j,共 次。 - 内层操作中,
bisect.insort(初始构建)和suf.pop(删除元素)在 Python 的列表中都是 的操作(因为需要移动元素)。 - 二分查找是 。
- 总时间复杂度为 。
- 外层循环遍历
- 空间复杂度:
- 列表
suf最多存储 个元素。
- 列表
思考
-
为什么使用
bisect_right?- 题目要求
nums[i] < nums[k](严格小于)。 bisect_right(suf, pre)返回的是第一个大于pre的元素的位置。- 如果使用
bisect_left,当suf中存在等于pre的元素时,会返回该元素的索引,导致nums[k] == nums[i],不满足条件。
- 题目要求
-
贪心策略:
- 在右侧寻找
nums[k]时,我们只需要找到任意一个满足pre < nums[k] < nums[j]的数即可。 - 通过
bisect_right找到的suf[idx]是右侧大于pre的最小数。 - 如果这个最小的候选数都小于
nums[j],那么一定存在解。 - 如果这个数大于等于
nums[j],那么右侧其他更大的数肯定也大于等于nums[j],无法满足nums[k] < nums[j]。
- 在右侧寻找
-
左侧最小值的维护:
pre始终代表nums[0...j-1]的最小值。- 这是为了让
nums[i]尽可能小,从而让nums[k]的选择范围(nums[k] > nums[i])尽可能大。
-
右侧集合的更新:
- 随着
j向右移动,原本属于“右侧”的nums[j+1]变成了“中间”的nums[j]。 - 因此,必须在每轮循环结束时将其从
suf中移除。
- 随着
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 笺札!






