力扣456. 132模式(数组记录最小,单调栈记录次小,枚举得到最大)
https://leetcode-cn.com/problems/132-pattern/
一、暴力
132 模式的三元组下标 (i, j, k)(i,j,k),枚举其中的 22 个下标时间复杂度为 O(n^2)O(n2),很容易超出时间限制
class Solution {
public:
bool find132pattern(vector<int>& nums) {
int n = nums.size();
vector<int> minval(n, 0);
minval[0] = nums[0];
for (int i = 1; i < n; i++)
{
minval[i] = min(minval[i - 1], nums[i - 1]);
}
for (int i = 0; i < n; i++)
{
cout << minval[i] << " ";
}
cout << endl;
for (int j = 1; j < n - 1; j++)
{
for (int k = j + 1; k < n; k++)
{
if ((minval[j] < nums[k]) && (nums[k] < nums[j]))
{
return true;
}
}
}
return false;
}
};
二、数组记录最小,单调栈记录次小,枚举得到最大
单调栈记录比元素大的值,或者是记录比元素次小、次次小的值。
思路
- 遍历的位置 j 相当于 132 模式中的 3,即
nums[j]
; - 找到 3 左边的最小元素 为 1,即
nums[i]
; - 找到 3 右边的比 3 小的最大元素 为 2,即
nums[k]
;
在方法一的做法中,是使用暴力求解得到的 2,很显然时间复杂度比较高。我们想要的 2 其实满足两个条件:
- 比 3 小;
- 在 nums[j+1 .. N-1]nums[j+1..N−1] 区间的最大元素。
为了找到这样的元素,我们可以使用一个单调递减的「栈」。所谓「单调栈」就是栈中的元素都是依次递增或者递减的,从而方便我们能维护好数组的一个区间内的「最大值」「次大值」等等。
想要求比 3 小的最大元素,则需要一个单调递减的栈。这样的话,最大元素在栈底,次大元素在栈底的第二元素……
具体到本题的实现方式:
- 求任何位置的左边最小的元素
nums[i]
,可以提前遍历一次而得到; - 使用「单调递减栈」,从右到左,把
nums[j]
入栈时,需要把栈里面比它小的元素全都 pop 出来,由于越往栈底越大,所以 pop 出的最后一个元素,就是比 3 小的最大元素nums[k]
。 - 判断如果
nums[i] < nums[k]
,那就说明得到了一个 132 模式。
因为单调栈是建立在 3 的右边的,因此,我们使用从右向左遍历。
当我们遍历到一个位置 i 需要寻找数组中左边或者右边的所有数字和 nums[i] 的大小关系的题目,可以考虑一下单调栈
class Solution {
public:
bool find132pattern(vector<int>& nums) {
int n = nums.size();
vector<int> minval(n, 0);
minval[0] = nums[0];
for (int i = 1; i < n; i++)
{
minval[i] = min(minval[i - 1], nums[i - 1]);
}
for (int i = 0; i < n; i++)
{
cout << minval[i] << " ";
}
cout << endl;
stack<int>sta;
sta.push(nums[n - 1]);
for (int j = n - 2; j >= 1; j--)
{
int temp = INT_MIN;
while (!sta.empty() && nums[j] > sta.top())
{
temp = sta.top();
sta.pop();
}
sta.push(nums[j]);
if (temp > minval[j])
{
return true;
}
}
return false;
}
};