[LeetCode] 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.
Note: n will be less than 15,000.
Example 1:
Input: [1, 2, 3, 4]
Output: False
Explanation: There is no 132 pattern in the sequence.
Example 2:
Input: [3, 1, 4, 2]
Output: True
Explanation: There is a 132 pattern in the sequence: [1, 4, 2].
Example 3:
Input: [-1, 3, 2, 0]
Output: True
Explanation: There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0]
and [-1, 2, 0].
132 模式。
给你一个整数数组 nums ,数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]、nums[j] 和 nums[k] 组成,并同时满足:i < j < k 和 nums[i] < nums[k] < nums[j] 。如果 nums 中存在 132 模式的子序列 ,返回 true ;否则,返回 false 。
进阶:很容易想到时间复杂度为 O(n^2) 的解决方案,你可以设计一个时间复杂度为 O(n logn) 或 O(n) 的解决方案吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/132-pattern
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路一 - treemap
我这里提供两种思路,一种是 treemap,一种是单调栈。这题如果没练过,面试当下大概率是想不出来的。
treemap 的做法,需要遍历两遍数组。第一次遍历 input 数组,统计每个数字出现了几次,用 treemap<key, count> 记录好。再一次遍历 input 数组,此时遇到一个数字,就从 treemap 中减去这个数字的 count,直到把这个数字在 treemap 中的 key 移除为止。在第二次遍历的过程中,记录一个最小值 min。如果在过程中发现有一个数字 num 比 min 大,同时在 treemap 中可以找到另一个数字既严格大于 min(treemap.higherKey(min)),又小于当前的 num,则说明找到了这个 132 模式。其中 treemap 的 higherKey() 函数是表示找一个严格大于当前数字的 key,注意它和 ceilingKey() 的区别。
复杂度
时间O(nlogn) - treemap 找 higherKey 的时间复杂度
空间O(n)
代码
Java实现
class Solution {
public boolean find132pattern(int[] nums) {
TreeMap<Integer, Integer> treemap = new TreeMap<>();
for (int num : nums) {
treemap.put(num, treemap.getOrDefault(num, 0) + 1);
}
int min = Integer.MAX_VALUE;
for (int num : nums) {
int count = treemap.get(num);
if (count > 1) {
treemap.put(num, count - 1);
} else {
treemap.remove(num);
}
if (num <= min) {
min = num;
} else {
// min is 1
// num is 3
// target is 2
Integer target = treemap.higherKey(min);
if (target != null && target < num) {
return true;
}
}
}
return false;
}
}
思路二 - 单调栈
第二种方法是单调栈,而且是从后往前
遍历数组。遍历过程中,记录一个变量 second 表示中间那个数字。因为这个数字需要出现在 1 和 3 之后,所以从后往前遍历是说得通的。对于任何一个数字nums[i]
,都无条件把他放入 stack 中。当 stack 不为空的时候,判断当前数字 nums[i]
是否大于 stack.peek()
,若大于则 stack.pop()
并且把 pop 出来的元素赋给 second。此时 pop 出来的元素因为是比较靠后的,所以其实 second 记录的是 132 模式里的那个 2。我们试图找一个 3 之后最大的 2,这样会留更多余地给 1。所以再往前扫描的时候,一旦发现有 nums[i] < mid 则 return true。
复杂度
时间O(n)
空间O(n)
代码
Java实现
class Solution {
public boolean find132pattern(int[] nums) {
Deque<Integer> stack = new ArrayDeque<>();
int n = nums.length;
int second = Integer.MIN_VALUE;
for (int i = n - 1; i >= 0; i--) {
int num = nums[i];
while (!stack.isEmpty() && stack.peekLast() < num) {
second = Math.max(second, stack.pollLast());
}
stack.offerLast(num);
if (num < second) {
return true;
}
}
return false;
}
}