各位题友大家好! 今天是 @负雪明烛 坚持日更的第 59 天。今天力扣上的每日一题是「456. 132模式」。

解题思路

今天这个题目很有意思,个人认为难度在中上。题目的 n 的范围是 15000,但是我测试了 $O(N^2)$ 复杂度的代码也不会超时。

题目要找 132 模式的组合,也就是对于 $i < j < k$ 有 $nums[i] < nums[k] < nums[j]$。下面我写了两种方法,方法一比较暴力,方法二性能比较高。

我选择的方法是维护 132模式 中间的那个数字 3,因为 3 在 132 的中间的数字也是最大的数字。我们的思路是个贪心的方法:我们要维护的 1 是 3 左边的最小的数字;2 是 3 右边的介于 1 和 3 之间的数字。

方法一:使用暴力维护 3

这个方法就是 $O(N^2)$ 的解法,是这个题的暴力解法。

从左到右遍历一次,遍历的数字是 nums[j] 也就是 132 模式中的 3。根据上面的贪心思想分析,我们想让 1 是 3 左边最小的元素,然后使用暴力在 $nums[j+1 .. N-1]$ 中找到 132 模式中的 2 就行。

这个思路比较简单,也能 AC。

  1. class Solution(object):
  2. def find132pattern(self, nums):
  3. N = len(nums)
  4. numsi = nums[0]
  5. for j in range(1, N):
  6. for k in range(N - 1, j, -1):
  7. if numsi < nums[k] and nums[k] < nums[j]:
  8. return True
  9. numsi = min(numsi, nums[j])
  10. return False
  • 时间复杂度:$O(N^2)$
  • 空间复杂度:$O(1)$

方法二:使用单调栈维护 3

如果我们维护的是 132 模式中的 3,那么就希望 1 尽可能小,2 尽可能大。

所以思路是这样的:

  1. 遍历的位置 j 相当于 132 模式中的 3,即 nums[j]
    2. 找到 3 左边的最小元素 为 1,即 nums[i]
    3. 找到 3 右边比 3 小的最大元素 为 2,即 nums[k]

在方法一的做法中,是使用暴力求解得到的 2,很显然时间复杂度比较高。我们想要的 2 其实满足两个条件:

  • 比 3 小;
  • 在 $nums[j+1 .. N-1]$ 区间的最大元素。

为了找到这样的元素,我们可以使用一个单调递减的「栈」。所谓「单调栈」就是栈中的元素都是依次递增或者递减的,从而方便我们能维护好数组的一个区间内的「最大值」「次大值」等等。

想要求「次大值」,则需要一个单调递减的栈。这样的话,最大元素在栈底,次大元素在栈底的第二元素……假如用的是单调递增的栈,那么最大元素在栈顶,比它更小的元素到了之后,则需要插入栈的中间位置,很明显不合理。

具体到本题的实现方式:

  • 求任何位置的左边最小的元素 nums[i] ,可以提前遍历一次而得到;
  • 使用「单调递减栈」,把 nums[j] 入栈时,需要把栈里面比它小的元素全都 pop 出来,由于越往栈底越大,所以 pop 出的最后一个元素,就是比 3 小的最大元素 nums[k]
  • 判断如果 nums[i] < nums[k] ,那就说明得到了一个 132 模式。

因为单调栈是建立在 3 的右边的,因此,我们使用从右向左遍历。

  1. class Solution(object):
  2. def find132pattern(self, nums):
  3. N = len(nums)
  4. leftMin = [float("inf")] * N
  5. for i in range(1, N):
  6. leftMin[i] = min(leftMin[i - 1], nums[i - 1])
  7. stack = []
  8. for j in range(N - 1, -1, -1):
  9. numsk = float("-inf")
  10. while stack and stack[-1] < nums[j]:
  11. numsk = stack.pop()
  12. if leftMin[j] < numsk:
  13. return True
  14. stack.append(nums[j])
  15. return False
  • 时间复杂度:$O(N)$
  • 空间复杂度:$O(N)$

刷题心得

今天的单调栈的使用还是有点技巧的,当我们遍历到一个位置 $i$ 需要寻找数组中左边或者右边的所有数字和 $nums[i]$ 的大小关系的题目,可以考虑一下单调栈。

参考资料:


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

祝大家牛年大吉!AC 多多,Offer 多多!我们明天再见!